From fabc7523984dc6ddc9b378badad7284c636e40eb Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 25 Jul 2024 23:44:06 +0200 Subject: [PATCH] Extract reading and parsing of arguments for short URLs data in commands --- .../ShortUrl/CreateShortUrlCommand.php | 84 ++++---------- module/CLI/src/Input/ShortUrlDataInput.php | 106 ++++++++++++++++++ .../Action/ShortUrl/EditShortUrlAction.php | 4 +- 3 files changed, 128 insertions(+), 66 deletions(-) create mode 100644 module/CLI/src/Input/ShortUrlDataInput.php diff --git a/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php b/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php index 4b6a088d..fa7a2bea 100644 --- a/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php +++ b/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Shlinkio\Shlink\CLI\Command\ShortUrl; +use Shlinkio\Shlink\CLI\Input\ShortUrlDataInput; use Shlinkio\Shlink\CLI\Util\ExitCode; use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException; use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; @@ -12,16 +13,11 @@ use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter; use Shlinkio\Shlink\Core\ShortUrl\UrlShortenerInterface; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use function array_map; -use function array_unique; -use function explode; -use function Shlinkio\Shlink\Core\ArrayUtils\flatten; use function sprintf; class CreateShortUrlCommand extends Command @@ -29,6 +25,7 @@ class CreateShortUrlCommand extends Command public const NAME = 'short-url:create'; private ?SymfonyStyle $io; + private readonly ShortUrlDataInput $shortUrlDataInput; public function __construct( private readonly UrlShortenerInterface $urlShortener, @@ -36,6 +33,7 @@ class CreateShortUrlCommand extends Command private readonly UrlShortenerOptions $options, ) { parent::__construct(); + $this->shortUrlDataInput = new ShortUrlDataInput($this); } protected function configure(): void @@ -43,26 +41,11 @@ class CreateShortUrlCommand extends Command $this ->setName(self::NAME) ->setDescription('Generates a short URL for provided long URL and returns it') - ->addArgument('longUrl', InputArgument::REQUIRED, 'The long URL to parse') ->addOption( - 'tags', - 't', - InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, - 'Tags to apply to the new short URL', - ) - ->addOption( - 'valid-since', - 's', + 'domain', + 'd', InputOption::VALUE_REQUIRED, - 'The date from which this short URL will be valid. ' - . 'If someone tries to access it before this date, it will not be found.', - ) - ->addOption( - 'valid-until', - 'u', - InputOption::VALUE_REQUIRED, - 'The date until which this short URL will be valid. ' - . 'If someone tries to access it after this date, it will not be found.', + 'The domain to which this short URL will be attached.', ) ->addOption( 'custom-slug', @@ -70,30 +53,6 @@ class CreateShortUrlCommand extends Command InputOption::VALUE_REQUIRED, 'If provided, this slug will be used instead of generating a short code', ) - ->addOption( - 'path-prefix', - 'p', - InputOption::VALUE_REQUIRED, - 'Prefix to prepend before the generated short code or provided custom slug', - ) - ->addOption( - 'max-visits', - 'm', - InputOption::VALUE_REQUIRED, - 'This will limit the number of visits for this short URL.', - ) - ->addOption( - 'find-if-exists', - 'f', - InputOption::VALUE_NONE, - 'This will force existing matching URL to be returned if found, instead of creating a new one.', - ) - ->addOption( - 'domain', - 'd', - InputOption::VALUE_REQUIRED, - 'The domain to which this short URL will be attached.', - ) ->addOption( 'short-code-length', 'l', @@ -101,16 +60,16 @@ class CreateShortUrlCommand extends Command 'The length for generated short code (it will be ignored if --custom-slug was provided).', ) ->addOption( - 'crawlable', - 'r', - InputOption::VALUE_NONE, - 'Tells if this URL will be included as "Allow" in Shlink\'s robots.txt.', + 'path-prefix', + 'p', + InputOption::VALUE_REQUIRED, + 'Prefix to prepend before the generated short code or provided custom slug', ) ->addOption( - 'no-forward-query', - 'w', + 'find-if-exists', + 'f', InputOption::VALUE_NONE, - 'Disables the forwarding of the query string to the long URL, when the new short URL is visited.', + 'This will force existing matching URL to be returned if found, instead of creating a new one.', ); } @@ -136,31 +95,28 @@ class CreateShortUrlCommand extends Command protected function execute(InputInterface $input, OutputInterface $output): int { $io = $this->getIO($input, $output); - $longUrl = $input->getArgument('longUrl'); + $longUrl = $this->shortUrlDataInput->longUrl($input); if (empty($longUrl)) { $io->error('A URL was not provided!'); return ExitCode::EXIT_FAILURE; } - $explodeWithComma = static fn (string $tag) => explode(',', $tag); - $tags = array_unique(flatten(array_map($explodeWithComma, $input->getOption('tags')))); - $maxVisits = $input->getOption('max-visits'); $shortCodeLength = $input->getOption('short-code-length') ?? $this->options->defaultShortCodesLength; try { $result = $this->urlShortener->shorten(ShortUrlCreation::fromRawData([ ShortUrlInputFilter::LONG_URL => $longUrl, - ShortUrlInputFilter::VALID_SINCE => $input->getOption('valid-since'), - ShortUrlInputFilter::VALID_UNTIL => $input->getOption('valid-until'), - ShortUrlInputFilter::MAX_VISITS => $maxVisits !== null ? (int) $maxVisits : null, + ShortUrlInputFilter::VALID_SINCE => $this->shortUrlDataInput->validSince($input), + ShortUrlInputFilter::VALID_UNTIL => $this->shortUrlDataInput->validUntil($input), + ShortUrlInputFilter::MAX_VISITS => $this->shortUrlDataInput->maxVisits($input), ShortUrlInputFilter::CUSTOM_SLUG => $input->getOption('custom-slug'), ShortUrlInputFilter::PATH_PREFIX => $input->getOption('path-prefix'), ShortUrlInputFilter::FIND_IF_EXISTS => $input->getOption('find-if-exists'), ShortUrlInputFilter::DOMAIN => $input->getOption('domain'), ShortUrlInputFilter::SHORT_CODE_LENGTH => $shortCodeLength, - ShortUrlInputFilter::TAGS => $tags, - ShortUrlInputFilter::CRAWLABLE => $input->getOption('crawlable'), - ShortUrlInputFilter::FORWARD_QUERY => !$input->getOption('no-forward-query'), + ShortUrlInputFilter::TAGS => $this->shortUrlDataInput->tags($input), + ShortUrlInputFilter::CRAWLABLE => $this->shortUrlDataInput->crawlable($input), + ShortUrlInputFilter::FORWARD_QUERY => !$this->shortUrlDataInput->noForwardQuery($input), ], $this->options)); $result->onEventDispatchingError(static fn () => $io->isVerbose() && $io->warning( diff --git a/module/CLI/src/Input/ShortUrlDataInput.php b/module/CLI/src/Input/ShortUrlDataInput.php new file mode 100644 index 00000000..5ba3126f --- /dev/null +++ b/module/CLI/src/Input/ShortUrlDataInput.php @@ -0,0 +1,106 @@ +addOption('long-url', 'l', InputOption::VALUE_REQUIRED, 'The long URL to set'); + } else { + $command->addArgument('longUrl', InputArgument::REQUIRED, 'The long URL to set'); + } + + $command + ->addOption( + 'tags', + 't', + InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, + 'Tags to apply to the short URL', + ) + ->addOption( + 'valid-since', + 's', + InputOption::VALUE_REQUIRED, + 'The date from which this short URL will be valid. ' + . 'If someone tries to access it before this date, it will not be found.', + ) + ->addOption( + 'valid-until', + 'u', + InputOption::VALUE_REQUIRED, + 'The date until which this short URL will be valid. ' + . 'If someone tries to access it after this date, it will not be found.', + ) + ->addOption( + 'max-visits', + 'm', + InputOption::VALUE_REQUIRED, + 'This will limit the number of visits for this short URL.', + ) + ->addOption( + 'crawlable', + 'r', + InputOption::VALUE_NONE, + 'Tells if this short URL will be included as "Allow" in Shlink\'s robots.txt.', + ) + ->addOption( + 'no-forward-query', + 'w', + InputOption::VALUE_NONE, + 'Disables the forwarding of the query string to the long URL, when the short URL is visited.', + ); + } + + public function longUrl(InputInterface $input): ?string + { + return $this->longUrlAsOption ? $input->getOption('long-url') : $input->getArgument('longUrl'); + } + + /** + * @return string[] + */ + public function tags(InputInterface $input): array + { + return array_unique(flatten(array_map(splitByComma(...), $input->getOption('tags')))); + } + + public function validSince(InputInterface $input): ?string + { + return $input->getOption('valid-since'); + } + + public function validUntil(InputInterface $input): ?string + { + return $input->getOption('valid-until'); + } + + public function maxVisits(InputInterface $input): ?int + { + $maxVisits = $input->getOption('max-visits'); + return $maxVisits !== null ? (int) $maxVisits : null; + } + + public function crawlable(InputInterface $input): bool + { + return $input->getOption('crawlable'); + } + + public function noForwardQuery(InputInterface $input): bool + { + return $input->getOption('no-forward-query'); + } +} diff --git a/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php b/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php index 61c1a70c..f0f8c068 100644 --- a/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php @@ -20,8 +20,8 @@ class EditShortUrlAction extends AbstractRestAction protected const ROUTE_ALLOWED_METHODS = [self::METHOD_PATCH]; public function __construct( - private ShortUrlServiceInterface $shortUrlService, - private DataTransformerInterface $transformer, + private readonly ShortUrlServiceInterface $shortUrlService, + private readonly DataTransformerInterface $transformer, ) { }