diff --git a/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php b/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php index 2e52571b..a5133d74 100644 --- a/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php +++ b/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php @@ -4,109 +4,42 @@ declare(strict_types=1); namespace Shlinkio\Shlink\CLI\Command\ShortUrl; -use Shlinkio\Shlink\CLI\Input\ShortUrlDataInput; +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\UrlShortenerInterface; +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\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use function sprintf; +#[AsCommand( + name: CreateShortUrlCommand::NAME, + description: 'Generates a short URL for provided long URL and returns it', +)] class CreateShortUrlCommand extends Command { public const string NAME = 'short-url:create'; - private SymfonyStyle $io; - private readonly ShortUrlDataInput $shortUrlDataInput; - public function __construct( private readonly UrlShortenerInterface $urlShortener, private readonly ShortUrlStringifierInterface $stringifier, private readonly UrlShortenerOptions $options, ) { parent::__construct(); - $this->shortUrlDataInput = new ShortUrlDataInput($this); } - protected function configure(): void + public function __invoke(SymfonyStyle $io, #[MapInput] ShortUrlCreationInput $inputData): int { - $this - ->setName(self::NAME) - ->setDescription('Generates a short URL for provided long URL and returns it') - ->addOption( - 'domain', - 'd', - InputOption::VALUE_REQUIRED, - 'The domain to which this short URL will be attached.', - ) - ->addOption( - 'custom-slug', - 'c', - InputOption::VALUE_REQUIRED, - 'If provided, this slug will be used instead of generating a short code', - ) - ->addOption( - 'short-code-length', - 'l', - InputOption::VALUE_REQUIRED, - 'The length for generated short code (it will be ignored if --custom-slug was provided).', - ) - ->addOption( - 'path-prefix', - 'p', - InputOption::VALUE_REQUIRED, - 'Prefix to prepend before the generated short code or provided custom slug', - ) - ->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.', - ); - } - - protected function interact(InputInterface $input, OutputInterface $output): void - { - $this->verifyLongUrlArgument($input, $output); - } - - private function verifyLongUrlArgument(InputInterface $input, OutputInterface $output): void - { - $longUrl = $input->getArgument('longUrl'); - if (! empty($longUrl)) { - return; - } - - $io = $this->getIO($input, $output); - $longUrl = $io->ask('Which URL do you want to shorten?'); - if (! empty($longUrl)) { - $input->setArgument('longUrl', $longUrl); - } - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $io = $this->getIO($input, $output); - try { - $result = $this->urlShortener->shorten($this->shortUrlDataInput->toShortUrlCreation( - $input, - $this->options, - customSlugField: 'custom-slug', - shortCodeLengthField: 'short-code-length', - pathPrefixField: 'path-prefix', - findIfExistsField: 'find-if-exists', - domainField: 'domain', - )); + $result = $this->urlShortener->shorten($inputData->toShortUrlCreation($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 ' - . 'short URL from the command line. Migrate to roadrunner in order to bypass this limitation.', + . 'short URL from the command line. Migrate to roadrunner in order to bypass this limitation.', )); $io->writeln([ @@ -119,9 +52,4 @@ class CreateShortUrlCommand extends Command return self::FAILURE; } } - - private function getIO(InputInterface $input, OutputInterface $output): SymfonyStyle - { - return $this->io ??= new SymfonyStyle($input, $output); - } } diff --git a/module/CLI/src/Command/ShortUrl/Input/ShortUrlCreationInput.php b/module/CLI/src/Command/ShortUrl/Input/ShortUrlCreationInput.php new file mode 100644 index 00000000..729c8a94 --- /dev/null +++ b/module/CLI/src/Command/ShortUrl/Input/ShortUrlCreationInput.php @@ -0,0 +1,57 @@ +shortCodeLength ?? $options->defaultShortCodesLength; + return ShortUrlCreation::fromRawData([ + ShortUrlInputFilter::LONG_URL => $this->longUrl, + ShortUrlInputFilter::DOMAIN => $this->domain, + ShortUrlInputFilter::CUSTOM_SLUG => $this->customSlug, + ShortUrlInputFilter::SHORT_CODE_LENGTH => $shortCodeLength, + ShortUrlInputFilter::PATH_PREFIX => $this->pathPrefix, + ShortUrlInputFilter::FIND_IF_EXISTS => $this->findIfExists, + ...$this->commonData->toArray(), + ], $options); + } +} diff --git a/module/CLI/src/Command/ShortUrl/Input/ShortUrlDataInput.php b/module/CLI/src/Command/ShortUrl/Input/ShortUrlDataInput.php new file mode 100644 index 00000000..988e87a0 --- /dev/null +++ b/module/CLI/src/Command/ShortUrl/Input/ShortUrlDataInput.php @@ -0,0 +1,80 @@ +validSince !== null) { + $data[ShortUrlInputFilter::VALID_SINCE] = $this->validSince; + } + if ($this->validUntil !== null) { + $data[ShortUrlInputFilter::VALID_UNTIL] = $this->validUntil; + } + if ($this->maxVisits !== null) { + $data[ShortUrlInputFilter::MAX_VISITS] = $this->maxVisits; + } + if ($this->tags !== null) { + $data[ShortUrlInputFilter::TAGS] = array_unique($this->tags); + } + if ($this->title !== null) { + $data[ShortUrlInputFilter::TITLE] = $this->title; + } + if ($this->crawlable !== null) { + $data[ShortUrlInputFilter::CRAWLABLE] = $this->crawlable; + } + if ($this->noForwardQuery !== null) { + $data[ShortUrlInputFilter::FORWARD_QUERY] = !$this->noForwardQuery; + } + + return $data; + } +} diff --git a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php index 9452aad5..07dd1efb 100644 --- a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php @@ -54,7 +54,7 @@ class CreateShortUrlCommandTest extends TestCase ); $this->commandTester->execute([ - 'longUrl' => 'http://domain.com/foo/bar', + 'long-url' => 'http://domain.com/foo/bar', '--max-visits' => '3', ], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]); $output = $this->commandTester->getDisplay(); @@ -87,7 +87,7 @@ class CreateShortUrlCommandTest extends TestCase ); $this->stringifier->method('stringify')->willReturn(''); - $this->commandTester->execute(['longUrl' => 'http://domain.com/invalid', '--custom-slug' => 'my-slug']); + $this->commandTester->execute(['long-url' => 'http://domain.com/invalid', '--custom-slug' => 'my-slug']); $output = $this->commandTester->getDisplay(); self::assertEquals(Command::FAILURE, $this->commandTester->getStatusCode()); @@ -109,7 +109,7 @@ class CreateShortUrlCommandTest extends TestCase ); $this->commandTester->execute([ - 'longUrl' => 'http://domain.com/foo/bar', + 'long-url' => 'http://domain.com/foo/bar', '--tag' => ['foo', 'bar', 'baz', 'boo', 'zar', 'baz'], ]); $output = $this->commandTester->getDisplay(); @@ -129,7 +129,7 @@ class CreateShortUrlCommandTest extends TestCase )->willReturn(UrlShorteningResult::withoutErrorOnEventDispatching(ShortUrl::createFake())); $this->stringifier->method('stringify')->willReturn(''); - $input['longUrl'] = 'http://domain.com/foo/bar'; + $input['long-url'] = 'http://domain.com/foo/bar'; $this->commandTester->execute($input); self::assertEquals(Command::SUCCESS, $this->commandTester->getStatusCode()); @@ -156,7 +156,7 @@ class CreateShortUrlCommandTest extends TestCase )->willReturn(UrlShorteningResult::withoutErrorOnEventDispatching($shortUrl)); $this->stringifier->method('stringify')->willReturn(''); - $options['longUrl'] = 'http://domain.com/foo/bar'; + $options['long-url'] = 'http://domain.com/foo/bar'; $this->commandTester->execute($options); } @@ -178,7 +178,7 @@ class CreateShortUrlCommandTest extends TestCase ); $this->stringifier->method('stringify')->willReturn('stringified_short_url'); - $this->commandTester->execute(['longUrl' => 'http://domain.com/foo/bar'], ['verbosity' => $verbosity]); + $this->commandTester->execute(['long-url' => 'http://domain.com/foo/bar'], ['verbosity' => $verbosity]); $output = $this->commandTester->getDisplay(); $assert($output);