mirror of
https://github.com/shlinkio/shlink.git
synced 2026-08-02 09:29:19 -05:00
Map models in CLI via TreeMapper
This commit is contained in:
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\CLI;
|
||||
|
||||
use CuyZ\Valinor\Mapper\TreeMapper;
|
||||
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
|
||||
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||
use Shlinkio\Shlink\Common\Doctrine\NoDbNameConnectionFactory;
|
||||
@@ -87,12 +88,18 @@ return [
|
||||
ShortUrl\UrlShortener::class,
|
||||
ShortUrlStringifier::class,
|
||||
UrlShortenerOptions::class,
|
||||
TreeMapper::class,
|
||||
],
|
||||
Command\ShortUrl\EditShortUrlCommand::class => [
|
||||
ShortUrl\ShortUrlService::class,
|
||||
ShortUrlStringifier::class,
|
||||
TreeMapper::class,
|
||||
],
|
||||
Command\ShortUrl\EditShortUrlCommand::class => [ShortUrl\ShortUrlService::class, ShortUrlStringifier::class],
|
||||
Command\ShortUrl\ResolveUrlCommand::class => [ShortUrl\ShortUrlResolver::class],
|
||||
Command\ShortUrl\ListShortUrlsCommand::class => [
|
||||
ShortUrl\ShortUrlListService::class,
|
||||
ShortUrl\Transformer\ShortUrlDataTransformer::class,
|
||||
TreeMapper::class,
|
||||
],
|
||||
Command\ShortUrl\GetShortUrlVisitsCommand::class => [Visit\VisitsStatsHelper::class],
|
||||
Command\ShortUrl\DeleteShortUrlCommand::class => [ShortUrl\DeleteShortUrlService::class],
|
||||
|
||||
@@ -4,10 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
|
||||
|
||||
use CuyZ\Valinor\Mapper\TreeMapper;
|
||||
use Shlinkio\Shlink\CLI\Command\ShortUrl\Input\ShortUrlCreationInput;
|
||||
use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions;
|
||||
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\UrlShortenerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Attribute\MapInput;
|
||||
@@ -28,6 +30,7 @@ class CreateShortUrlCommand extends Command
|
||||
private readonly UrlShortenerInterface $urlShortener,
|
||||
private readonly ShortUrlStringifierInterface $stringifier,
|
||||
private readonly UrlShortenerOptions $options,
|
||||
private readonly TreeMapper $treeMapper,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
@@ -35,7 +38,9 @@ class CreateShortUrlCommand extends Command
|
||||
public function __invoke(SymfonyStyle $io, #[MapInput] ShortUrlCreationInput $inputData): int
|
||||
{
|
||||
try {
|
||||
$result = $this->urlShortener->shorten($inputData->toShortUrlCreation($this->options));
|
||||
$result = $this->urlShortener->shorten(
|
||||
$this->treeMapper->map(ShortUrlCreation::class, $inputData->toArray($this->options)),
|
||||
);
|
||||
|
||||
$result->onEventDispatchingError(static fn () => $io->isVerbose() && $io->warning(
|
||||
'Short URL properly created, but the real-time updates cannot be notified when generating the '
|
||||
|
||||
@@ -4,9 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
|
||||
|
||||
use CuyZ\Valinor\Mapper\TreeMapper;
|
||||
use Shlinkio\Shlink\CLI\Command\ShortUrl\Input\ShortUrlDataInput;
|
||||
use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlEdition;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\ShortUrlServiceInterface;
|
||||
use Symfony\Component\Console\Attribute\Argument;
|
||||
@@ -29,6 +31,7 @@ class EditShortUrlCommand extends Command
|
||||
public function __construct(
|
||||
private readonly ShortUrlServiceInterface $shortUrlService,
|
||||
private readonly ShortUrlStringifierInterface $stringifier,
|
||||
private readonly TreeMapper $treeMapper,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
@@ -45,7 +48,7 @@ class EditShortUrlCommand extends Command
|
||||
try {
|
||||
$shortUrl = $this->shortUrlService->updateShortUrl(
|
||||
$identifier,
|
||||
$data->toShortUrlEdition($longUrl),
|
||||
$this->treeMapper->map(ShortUrlEdition::class, $data->toArray($longUrl)),
|
||||
);
|
||||
|
||||
$io->success(sprintf('Short URL "%s" properly edited', $this->stringifier->stringify($shortUrl)));
|
||||
|
||||
@@ -5,14 +5,18 @@ declare(strict_types=1);
|
||||
namespace Shlinkio\Shlink\CLI\Command\ShortUrl\Input;
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation;
|
||||
use Symfony\Component\Console\Attribute\Argument;
|
||||
use Symfony\Component\Console\Attribute\Ask;
|
||||
use Symfony\Component\Console\Attribute\MapInput;
|
||||
use Symfony\Component\Console\Attribute\Option;
|
||||
|
||||
use function array_filter;
|
||||
use function get_object_vars;
|
||||
use function max;
|
||||
|
||||
use const ARRAY_FILTER_USE_KEY;
|
||||
use const Shlinkio\Shlink\MIN_SHORT_CODES_LENGTH;
|
||||
|
||||
/**
|
||||
* Data used for short URL creation
|
||||
*/
|
||||
@@ -42,18 +46,22 @@ final class ShortUrlCreationInput
|
||||
)]
|
||||
public bool $findIfExists = false;
|
||||
|
||||
public function toShortUrlCreation(UrlShortenerOptions $options): ShortUrlCreation
|
||||
public function toArray(UrlShortenerOptions $options): array
|
||||
{
|
||||
// TODO Should create using a TreeMapper
|
||||
$shortCodeLength = max(4, $this->shortCodeLength ?? $options->defaultShortCodesLength);
|
||||
return new ShortUrlCreation(
|
||||
$this->longUrl,
|
||||
...$this->commonData->toArray(),
|
||||
customSlug: $this->customSlug,
|
||||
pathPrefix: $this->pathPrefix,
|
||||
findIfExists: $this->findIfExists,
|
||||
domain: $this->domain,
|
||||
shortCodeLength: $shortCodeLength,
|
||||
$common = $this->commonData->toArray($this->longUrl);
|
||||
$creation = array_filter(
|
||||
get_object_vars($this),
|
||||
static fn (string $key) => $key !== 'commonData',
|
||||
ARRAY_FILTER_USE_KEY,
|
||||
);
|
||||
$shortCodeLength = max(MIN_SHORT_CODES_LENGTH, $this->shortCodeLength ?? $options->defaultShortCodesLength);
|
||||
|
||||
return [
|
||||
...$common,
|
||||
...$creation,
|
||||
'shortCodeLength' => $shortCodeLength,
|
||||
'shortUrlMode' => $options->mode,
|
||||
'multiSegmentSlugsEnabled' => $options->multiSegmentSlugsEnabled,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\CLI\Command\ShortUrl\Input;
|
||||
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlEdition;
|
||||
use Symfony\Component\Console\Attribute\Option;
|
||||
|
||||
use function array_filter;
|
||||
use function array_unique;
|
||||
use function get_object_vars;
|
||||
|
||||
/**
|
||||
* Common input used for short URL creation and edition
|
||||
@@ -16,7 +17,11 @@ final class ShortUrlDataInput
|
||||
{
|
||||
/** @var string[]|null */
|
||||
#[Option('Tags to apply to the short URL', name: 'tag', shortcut: 't')]
|
||||
public array|null $tags = null;
|
||||
// phpcs:disable PSR2.Classes.PropertyDeclaration.Multiple
|
||||
public array|null $tags = null {
|
||||
// phpcs:disable PSR2.Classes.PropertyDeclaration.Multiple, PSR2.Classes.PropertyDeclaration.ScopeMissing
|
||||
set(array | null $value) => $value !== null ? array_unique($value) : $value;
|
||||
}
|
||||
|
||||
#[Option(
|
||||
'The date from which this short URL will be valid. '
|
||||
@@ -47,56 +52,19 @@ final class ShortUrlDataInput
|
||||
)]
|
||||
public bool|null $noForwardQuery = null;
|
||||
|
||||
public function toArray(): array
|
||||
public function toArray(string|null $longUrl): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
// Avoid setting arguments that were not explicitly provided.
|
||||
// This is important when editing short URLs and should not make a difference when creating.
|
||||
if ($this->validSince !== null) {
|
||||
$data['validSince'] = $this->validSince;
|
||||
}
|
||||
if ($this->validUntil !== null) {
|
||||
$data['validUntil'] = $this->validUntil;
|
||||
}
|
||||
if ($this->maxVisits !== null) {
|
||||
$data['maxVisits'] = $this->maxVisits;
|
||||
}
|
||||
if ($this->tags !== null) {
|
||||
$data['tags'] = array_unique($this->tags);
|
||||
}
|
||||
if ($this->title !== null) {
|
||||
$data['title'] = $this->title;
|
||||
}
|
||||
if ($this->crawlable !== null) {
|
||||
$data['crawlable'] = $this->crawlable;
|
||||
}
|
||||
if ($this->noForwardQuery !== null) {
|
||||
$data['forwardQuery'] = !$this->noForwardQuery;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function toShortUrlEdition(string|null $longUrl): ShortUrlEdition
|
||||
{
|
||||
return new ShortUrlEdition(
|
||||
longUrlWasProvided: $longUrl !== null,
|
||||
longUrl: $longUrl,
|
||||
validSinceWasProvided: $this->validSince !== null,
|
||||
validSince: $this->validSince,
|
||||
validUntilWasProvided: $this->validUntil !== null,
|
||||
validUntil: $this->validUntil,
|
||||
maxVisitsWasProvided: $this->maxVisits !== null,
|
||||
maxVisits: $this->maxVisits,
|
||||
tagsWereProvided: $this->tags !== null,
|
||||
tags: $this->tags ?? [],
|
||||
titleWasProvided: $this->title !== null,
|
||||
title: $this->title,
|
||||
crawlableWasProvided: $this->crawlable !== null,
|
||||
crawlable: $this->crawlable ?? false,
|
||||
forwardQueryWasProvided: $this->noForwardQuery !== null,
|
||||
forwardQuery: $this->noForwardQuery !== null && ! $this->noForwardQuery,
|
||||
);
|
||||
return [
|
||||
...array_filter(get_object_vars($this), static fn (mixed $value) => $value !== null),
|
||||
'longUrl' => $longUrl,
|
||||
'longUrlWasProvided' => $longUrl !== null,
|
||||
'validSinceWasProvided' => $this->validSince !== null,
|
||||
'validUntilWasProvided' => $this->validUntil !== null,
|
||||
'maxVisitsWasProvided' => $this->maxVisits !== null,
|
||||
'tagsWereProvided' => $this->tags !== null,
|
||||
'titleWasProvided' => $this->title !== null,
|
||||
'crawlableWasProvided' => $this->crawlable !== null,
|
||||
'forwardQueryWasProvided' => $this->noForwardQuery !== null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,13 @@ declare(strict_types=1);
|
||||
namespace Shlinkio\Shlink\CLI\Command\ShortUrl\Input;
|
||||
|
||||
use Shlinkio\Shlink\CLI\Command\ShortUrl\ListShortUrlsCommand;
|
||||
use Shlinkio\Shlink\CLI\Input\InputUtils;
|
||||
use Shlinkio\Shlink\Common\Paginator\Paginator;
|
||||
use Shlinkio\Shlink\Core\Domain\Entity\Domain;
|
||||
use Shlinkio\Shlink\Core\Model\Ordering;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlsParams;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\TagsMode;
|
||||
use Symfony\Component\Console\Attribute\Option;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use function get_object_vars;
|
||||
|
||||
/**
|
||||
* Input arguments and options for short-url:list command
|
||||
@@ -29,6 +28,12 @@ final class ShortUrlsParamsInput
|
||||
)]
|
||||
public bool $all = false;
|
||||
|
||||
// phpcs:disable PSR2.Classes.PropertyDeclaration.Multiple
|
||||
public int $itemsPerPage {
|
||||
// phpcs:disable PSR2.Classes.PropertyDeclaration.ScopeMissing
|
||||
get => $this->all ? Paginator::ALL_ITEMS : ShortUrlsParams::DEFAULT_ITEMS_PER_PAGE;
|
||||
}
|
||||
|
||||
#[Option('Only return short URLs older than this date', shortcut: 's')]
|
||||
public string|null $startDate = null;
|
||||
|
||||
@@ -44,20 +49,32 @@ final class ShortUrlsParamsInput
|
||||
)]
|
||||
public string|null $domain = null;
|
||||
|
||||
/** @var string[]|null */
|
||||
/** @var string[] */
|
||||
#[Option('A list of tags that short URLs need to include', name: 'tag', shortcut: 't')]
|
||||
public array|null $tags = null;
|
||||
public array $tags = [];
|
||||
|
||||
#[Option('If --tag is provided, returns only short URLs including ALL of them')]
|
||||
public bool $tagsAll = false;
|
||||
|
||||
/** @var string[]|null */
|
||||
// phpcs:disable PSR2.Classes.PropertyDeclaration.Multiple
|
||||
public TagsMode $tagsMode {
|
||||
// phpcs:disable PSR2.Classes.PropertyDeclaration.ScopeMissing
|
||||
get => $this->tagsAll ? TagsMode::ALL : TagsMode::ANY;
|
||||
}
|
||||
|
||||
/** @var string[] */
|
||||
#[Option('A list of tags that short URLs should NOT include', name: 'exclude-tag', shortcut: 'et')]
|
||||
public array|null $excludeTags = null;
|
||||
public array $excludeTags = [];
|
||||
|
||||
#[Option('If --exclude-tag is provided, returns only short URLs not including ANY of them')]
|
||||
public bool $excludeTagsAll = false;
|
||||
|
||||
// phpcs:disable PSR2.Classes.PropertyDeclaration.Multiple
|
||||
public TagsMode $excludeTagsMode {
|
||||
// phpcs:disable PSR2.Classes.PropertyDeclaration.ScopeMissing
|
||||
get => $this->excludeTagsAll ? TagsMode::ALL : TagsMode::ANY;
|
||||
}
|
||||
|
||||
#[Option('Excludes short URLs which reached their max amount of visits')]
|
||||
public bool $excludeMaxVisitsReached = false;
|
||||
|
||||
@@ -85,23 +102,8 @@ final class ShortUrlsParamsInput
|
||||
#[Option('Whether to display the API key name from which the URL was generated or not', shortcut: 'k')]
|
||||
public bool $showApiKey = false;
|
||||
|
||||
public function toParams(OutputInterface $output): ShortUrlsParams
|
||||
public function toArray(): array
|
||||
{
|
||||
return new ShortUrlsParams(
|
||||
page: $this->page,
|
||||
itemsPerPage: $this->all ? Paginator::ALL_ITEMS : ShortUrlsParams::DEFAULT_ITEMS_PER_PAGE,
|
||||
searchTerm: $this->searchTerm,
|
||||
tags: $this->tags ?? [],
|
||||
orderBy: Ordering::fromOptionalString($this->orderBy),
|
||||
startDate: InputUtils::processDate('start-date', $this->startDate, $output),
|
||||
endDate: InputUtils::processDate('end-date', $this->endDate, $output),
|
||||
excludeMaxVisitsReached: $this->excludeMaxVisitsReached,
|
||||
excludePastValidUntil: $this->excludePastValidUntil,
|
||||
tagsMode: $this->tagsAll ? TagsMode::ALL : TagsMode::ANY,
|
||||
domain: $this->domain,
|
||||
excludeTags: $this->excludeTags ?? [],
|
||||
excludeTagsMode: $this->excludeTagsAll ? TagsMode::ALL : TagsMode::ANY,
|
||||
apiKeyName: $this->apiKeyName,
|
||||
);
|
||||
return get_object_vars($this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
|
||||
|
||||
use CuyZ\Valinor\Mapper\TreeMapper;
|
||||
use Shlinkio\Shlink\CLI\Command\ShortUrl\Input\ShortUrlsParamsInput;
|
||||
use Shlinkio\Shlink\CLI\Util\ShlinkTable;
|
||||
use Shlinkio\Shlink\Common\Paginator\Paginator;
|
||||
@@ -17,7 +18,6 @@ use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Attribute\MapInput;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
@@ -34,18 +34,19 @@ class ListShortUrlsCommand extends Command
|
||||
public function __construct(
|
||||
private readonly ShortUrlListServiceInterface $shortUrlService,
|
||||
private readonly ShortUrlDataTransformerInterface $transformer,
|
||||
private readonly TreeMapper $treeMapper,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
InputInterface $input,
|
||||
#[MapInput] ShortUrlsParamsInput $paramsInput,
|
||||
): int {
|
||||
$columnsMap = $this->resolveColumnsMap($input);
|
||||
$columnsMap = $this->resolveColumnsMap($paramsInput);
|
||||
do {
|
||||
$result = $this->renderPage($io, $columnsMap, $paramsInput->toParams($io), $paramsInput->all);
|
||||
$params = $this->treeMapper->map(ShortUrlsParams::class, $paramsInput->toArray());
|
||||
$result = $this->renderPage($io, $columnsMap, $params, $paramsInput->all);
|
||||
$paramsInput->page += 1;
|
||||
|
||||
$continue = $result->hasNextPage() && $io->confirm(
|
||||
@@ -89,7 +90,7 @@ class ListShortUrlsCommand extends Command
|
||||
/**
|
||||
* @return array<string, callable(array $serializedShortUrl, ShortUrl $shortUrl): ?string>
|
||||
*/
|
||||
private function resolveColumnsMap(InputInterface $input): array
|
||||
private function resolveColumnsMap(ShortUrlsParamsInput $params): array
|
||||
{
|
||||
$pickProp = static fn (string $prop): callable => static fn (array $shortUrl) => $shortUrl[$prop];
|
||||
$columnsMap = [
|
||||
@@ -100,14 +101,14 @@ class ListShortUrlsCommand extends Command
|
||||
'Date created' => $pickProp('dateCreated'),
|
||||
'Visits count' => static fn (array $shortUrl) => $shortUrl['visitsSummary']->total,
|
||||
];
|
||||
if ($input->getOption('show-tags')) {
|
||||
if ($params->showTags) {
|
||||
$columnsMap['Tags'] = static fn (array $shortUrl): string => implode(', ', $shortUrl['tags']);
|
||||
}
|
||||
if ($input->getOption('show-domain')) {
|
||||
if ($params->showDomain) {
|
||||
$columnsMap['Domain'] = static fn (array $_, ShortUrl $shortUrl): string =>
|
||||
$shortUrl->getDomain()->authority ?? Domain::DEFAULT_AUTHORITY;
|
||||
}
|
||||
if ($input->getOption('show-api-key')) {
|
||||
if ($params->showApiKey) {
|
||||
$columnsMap['API Key Name'] = static fn (array $_, ShortUrl $shortUrl): string|null =>
|
||||
$shortUrl->authorApiKey?->name;
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\CLI\Input;
|
||||
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Throwable;
|
||||
|
||||
use function Shlinkio\Shlink\Common\normalizeOptionalDate;
|
||||
use function sprintf;
|
||||
|
||||
final class InputUtils
|
||||
{
|
||||
/**
|
||||
* Process a date provided via input params, and format it as ATOM.
|
||||
* A warning is printed if the date cannot be parsed, returning `null` in that case.
|
||||
*/
|
||||
public static function processDate(string $name, string|null $value, OutputInterface $output): string|null
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return normalizeOptionalDate($value)->toAtomString();
|
||||
} catch (Throwable) {
|
||||
$output->writeln(sprintf(
|
||||
'<comment>> Ignored provided "%s" since its value "%s" is not a valid date. <</comment>',
|
||||
$name,
|
||||
$value,
|
||||
));
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\CLI\Command\ShortUrl;
|
||||
|
||||
use CuyZ\Valinor\MapperBuilder;
|
||||
use Laminas\ServiceManager\Exception\ServiceNotFoundException;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
@@ -39,6 +40,7 @@ class CreateShortUrlCommandTest extends TestCase
|
||||
$this->urlShortener,
|
||||
$this->stringifier,
|
||||
new UrlShortenerOptions(defaultDomain: 'example.com', defaultShortCodesLength: 5),
|
||||
new MapperBuilder()->allowSuperfluousKeys()->mapper(),
|
||||
);
|
||||
$this->commandTester = CliTestUtils::testerForCommand($command);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace ShlinkioTest\Shlink\CLI\Command\ShortUrl;
|
||||
|
||||
use CuyZ\Valinor\MapperBuilder;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\Attributes\TestWith;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
@@ -29,7 +30,7 @@ class EditShortUrlCommandTest extends TestCase
|
||||
$this->shortUrlService = $this->createMock(ShortUrlServiceInterface::class);
|
||||
$this->stringifier = $this->createMock(ShortUrlStringifierInterface::class);
|
||||
|
||||
$command = new EditShortUrlCommand($this->shortUrlService, $this->stringifier);
|
||||
$command = new EditShortUrlCommand($this->shortUrlService, $this->stringifier, new MapperBuilder()->mapper());
|
||||
$this->commandTester = CliTestUtils::testerForCommand($command);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace ShlinkioTest\Shlink\CLI\Command\ShortUrl;
|
||||
|
||||
use Cake\Chronos\Chronos;
|
||||
use CuyZ\Valinor\MapperBuilder;
|
||||
use Pagerfanta\Adapter\ArrayAdapter;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
@@ -34,9 +35,11 @@ class ListShortUrlsCommandTest extends TestCase
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->shortUrlService = $this->createMock(ShortUrlListServiceInterface::class);
|
||||
$command = new ListShortUrlsCommand($this->shortUrlService, new ShortUrlDataTransformer(
|
||||
new ShortUrlStringifier(),
|
||||
));
|
||||
$command = new ListShortUrlsCommand(
|
||||
$this->shortUrlService,
|
||||
new ShortUrlDataTransformer(new ShortUrlStringifier()),
|
||||
new MapperBuilder()->allowSuperfluousKeys()->mapper(),
|
||||
);
|
||||
$this->commandTester = CliTestUtils::testerForCommand($command);
|
||||
}
|
||||
|
||||
@@ -72,7 +75,7 @@ class ListShortUrlsCommandTest extends TestCase
|
||||
}
|
||||
|
||||
$this->shortUrlService->expects($this->once())->method('listShortUrls')->with(
|
||||
ShortUrlsParams::empty(),
|
||||
new ShortUrlsParams(),
|
||||
)->willReturn(new Paginator(new ArrayAdapter($data)));
|
||||
|
||||
$this->commandTester->setInputs(['n']);
|
||||
@@ -107,7 +110,7 @@ class ListShortUrlsCommandTest extends TestCase
|
||||
ShortUrl $shortUrl,
|
||||
): void {
|
||||
$this->shortUrlService->expects($this->once())->method('listShortUrls')->with(
|
||||
ShortUrlsParams::empty(),
|
||||
new ShortUrlsParams(),
|
||||
)->willReturn(new Paginator(new ArrayAdapter([
|
||||
ShortUrlWithDeps::fromShortUrl($shortUrl),
|
||||
])));
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\CLI\Input;
|
||||
|
||||
use Cake\Chronos\Chronos;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\Attributes\TestWith;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shlinkio\Shlink\CLI\Input\InputUtils;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class InputUtilsTest extends TestCase
|
||||
{
|
||||
private MockObject & OutputInterface $input;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->input = $this->createMock(OutputInterface::class);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
#[TestWith([null], 'null')]
|
||||
#[TestWith([''], 'empty string')]
|
||||
public function processDateReturnsNullForEmptyDates(string|null $date): void
|
||||
{
|
||||
$this->input->expects($this->never())->method('writeln');
|
||||
self::assertNull(InputUtils::processDate('name', $date, $this->input));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function processDateReturnsAtomFormatedForValidDates(): void
|
||||
{
|
||||
$date = '2025-01-20';
|
||||
$this->input->expects($this->never())->method('writeln');
|
||||
self::assertEquals(Chronos::parse($date)->toAtomString(), InputUtils::processDate('name', $date, $this->input));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function warningIsPrintedWhenDateIsInvalid(): void
|
||||
{
|
||||
$this->input->expects($this->once())->method('writeln')->with(
|
||||
'<comment>> Ignored provided "name" since its value "invalid" is not a valid date. <</comment>',
|
||||
);
|
||||
self::assertNull(InputUtils::processDate('name', 'invalid', $this->input));
|
||||
}
|
||||
}
|
||||
@@ -54,12 +54,9 @@ final readonly class ShortUrlsParams
|
||||
normalizeOptionalDate($startDate),
|
||||
normalizeOptionalDate($endDate),
|
||||
);
|
||||
|
||||
// FIXME When using shlink-common, TagsConverter implicitly does an array_unique.
|
||||
$this->tags = array_unique($tags);
|
||||
$this->excludeTags = array_unique($excludeTags);
|
||||
}
|
||||
|
||||
public static function empty(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class ShortUrlListServiceTest extends TestCase
|
||||
$this->repo->expects($this->once())->method('findList')->willReturn($list);
|
||||
$this->repo->expects($this->once())->method('countList')->willReturn(count($list));
|
||||
|
||||
$paginator = $this->service->listShortUrls(ShortUrlsParams::empty(), $apiKey);
|
||||
$paginator = $this->service->listShortUrls(new ShortUrlsParams(), $apiKey);
|
||||
|
||||
self::assertCount(4, $paginator);
|
||||
self::assertCount(4, $paginator->getCurrentPageResults());
|
||||
|
||||
Reference in New Issue
Block a user