mirror of
https://github.com/shlinkio/shlink.git
synced 2026-02-28 04:03:12 +08:00
Merge pull request #2509 from acelaya-forks/invokable-commands
Invokable commands
This commit is contained in:
@@ -31,7 +31,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
* [#2472](https://github.com/shlinkio/shlink/issues/2472) Add support for PHP 8.5
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
* [#2424](https://github.com/shlinkio/shlink/issues/2424) Make simple console commands invokable.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
@@ -5,11 +5,15 @@ declare(strict_types=1);
|
||||
namespace Shlinkio\Shlink\CLI\Command\Api;
|
||||
|
||||
use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface;
|
||||
use Symfony\Component\Console\Attribute\Argument;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: InitialApiKeyCommand::NAME,
|
||||
description: 'Tries to create initial API key',
|
||||
)]
|
||||
class InitialApiKeyCommand extends Command
|
||||
{
|
||||
public const string NAME = 'api-key:initial';
|
||||
@@ -19,22 +23,14 @@ class InitialApiKeyCommand extends Command
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setHidden()
|
||||
->setName(self::NAME)
|
||||
->setDescription('Tries to create initial API key')
|
||||
->addArgument('apiKey', InputArgument::REQUIRED, 'The initial API to create');
|
||||
}
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
#[Argument('The initial API to create')] string $apiKey,
|
||||
): int {
|
||||
$result = $this->apiKeyService->createInitial($apiKey);
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$key = $input->getArgument('apiKey');
|
||||
$result = $this->apiKeyService->createInitial($key);
|
||||
|
||||
if ($result === null && $output->isVerbose()) {
|
||||
$output->writeln('<comment>Other API keys already exist. Initial API key creation skipped.</comment>');
|
||||
if ($result === null && $io->isVerbose()) {
|
||||
$io->writeln('<comment>Other API keys already exist. Initial API key creation skipped.</comment>');
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
|
||||
@@ -6,9 +6,10 @@ namespace Shlinkio\Shlink\CLI\Command\Config;
|
||||
|
||||
use Closure;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Symfony\Component\Console\Attribute\Argument;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
@@ -18,6 +19,11 @@ use function Shlinkio\Shlink\Core\ArrayUtils\contains;
|
||||
use function Shlinkio\Shlink\Core\enumValues;
|
||||
use function sprintf;
|
||||
|
||||
#[AsCommand(
|
||||
name: ReadEnvVarCommand::NAME,
|
||||
description: 'Display current value for an env var',
|
||||
hidden: true,
|
||||
)]
|
||||
class ReadEnvVarCommand extends Command
|
||||
{
|
||||
public const string NAME = 'env-var:read';
|
||||
@@ -31,19 +37,10 @@ class ReadEnvVarCommand extends Command
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName(self::NAME)
|
||||
->setHidden()
|
||||
->setDescription('Display current value for an env var')
|
||||
->addArgument('envVar', InputArgument::REQUIRED, 'The env var to read');
|
||||
}
|
||||
|
||||
protected function interact(InputInterface $input, OutputInterface $output): void
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$envVar = $input->getArgument('envVar');
|
||||
$envVar = $input->getArgument('env-var');
|
||||
$validEnvVars = enumValues(EnvVars::class);
|
||||
|
||||
if ($envVar === null) {
|
||||
@@ -54,14 +51,14 @@ class ReadEnvVarCommand extends Command
|
||||
throw new InvalidArgumentException(sprintf('%s is not a valid Shlink environment variable', $envVar));
|
||||
}
|
||||
|
||||
$input->setArgument('envVar', $envVar);
|
||||
$input->setArgument('env-var', $envVar);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$envVar = $input->getArgument('envVar');
|
||||
$output->writeln(formatEnvVarValue(($this->loadEnvVar)($envVar)));
|
||||
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
#[Argument(description: 'The env var to read')] string $envVar,
|
||||
): int {
|
||||
$io->writeln(formatEnvVarValue(($this->loadEnvVar)($envVar)));
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@ namespace Shlinkio\Shlink\CLI\Command\Domain;
|
||||
use Shlinkio\Shlink\Core\Config\NotFoundRedirects;
|
||||
use Shlinkio\Shlink\Core\Domain\DomainServiceInterface;
|
||||
use Shlinkio\Shlink\Core\Domain\Model\DomainItem;
|
||||
use Symfony\Component\Console\Attribute\Argument;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
@@ -18,6 +19,10 @@ use function array_map;
|
||||
use function sprintf;
|
||||
use function str_contains;
|
||||
|
||||
#[AsCommand(
|
||||
name: DomainRedirectsCommand::NAME,
|
||||
description: 'Set specific "not found" redirects for individual domains.',
|
||||
)]
|
||||
class DomainRedirectsCommand extends Command
|
||||
{
|
||||
public const string NAME = 'domain:redirects';
|
||||
@@ -27,18 +32,6 @@ class DomainRedirectsCommand extends Command
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName(self::NAME)
|
||||
->setDescription('Set specific "not found" redirects for individual domains.')
|
||||
->addArgument(
|
||||
'domain',
|
||||
InputArgument::REQUIRED,
|
||||
'The domain authority to which you want to set the specific redirects',
|
||||
);
|
||||
}
|
||||
|
||||
protected function interact(InputInterface $input, OutputInterface $output): void
|
||||
{
|
||||
/** @var string|null $domain */
|
||||
@@ -67,10 +60,11 @@ class DomainRedirectsCommand extends Command
|
||||
$input->setArgument('domain', str_contains($selectedOption, 'New domain') ? $askNewDomain() : $selectedOption);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$domainAuthority = $input->getArgument('domain');
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
#[Argument('The domain authority to which you want to set the specific redirects', name: 'domain')]
|
||||
string $domainAuthority,
|
||||
): int {
|
||||
$domain = $this->domainService->findByAuthority($domainAuthority);
|
||||
|
||||
$ask = static function (string $message, string|null $current) use ($io): string|null {
|
||||
|
||||
@@ -8,13 +8,17 @@ use Shlinkio\Shlink\CLI\Util\ShlinkTable;
|
||||
use Shlinkio\Shlink\Core\Config\NotFoundRedirectConfigInterface;
|
||||
use Shlinkio\Shlink\Core\Domain\DomainServiceInterface;
|
||||
use Shlinkio\Shlink\Core\Domain\Model\DomainItem;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Attribute\Option;
|
||||
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 array_map;
|
||||
|
||||
#[AsCommand(
|
||||
name: ListDomainsCommand::NAME,
|
||||
description: 'List all domains that have been ever used for some short URL',
|
||||
)]
|
||||
class ListDomainsCommand extends Command
|
||||
{
|
||||
public const string NAME = 'domain:list';
|
||||
@@ -24,25 +28,17 @@ class ListDomainsCommand extends Command
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName(self::NAME)
|
||||
->setDescription('List all domains that have been ever used for some short URL')
|
||||
->addOption(
|
||||
'show-redirects',
|
||||
'r',
|
||||
InputOption::VALUE_NONE,
|
||||
'Will display an extra column with the information of the "not found" redirects for every domain.',
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
#[Option(
|
||||
'Will display an extra column with the information of the "not found" redirects for every domain.',
|
||||
shortcut: 'r',
|
||||
)]
|
||||
bool $showRedirects = false,
|
||||
): int {
|
||||
$domains = $this->domainService->listDomains();
|
||||
$showRedirects = $input->getOption('show-redirects');
|
||||
$commonFields = ['Domain', 'Is default'];
|
||||
$table = $showRedirects ? ShlinkTable::withRowSeparators($output) : ShlinkTable::default($output);
|
||||
$table = $showRedirects ? ShlinkTable::withRowSeparators($io) : ShlinkTable::default($io);
|
||||
|
||||
$table->render(
|
||||
$showRedirects ? [...$commonFields, '"Not found" redirects'] : $commonFields,
|
||||
@@ -53,7 +49,7 @@ class ListDomainsCommand extends Command
|
||||
? [
|
||||
...$commonValues,
|
||||
$this->notFoundRedirectsToString($domain->notFoundRedirectConfig),
|
||||
]
|
||||
]
|
||||
: $commonValues;
|
||||
}, $domains),
|
||||
);
|
||||
|
||||
@@ -8,10 +8,10 @@ use Cake\Chronos\Chronos;
|
||||
use Shlinkio\Shlink\Core\Matomo\MatomoOptions;
|
||||
use Shlinkio\Shlink\Core\Matomo\MatomoVisitSenderInterface;
|
||||
use Shlinkio\Shlink\Core\Matomo\VisitSendingProgressTrackerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Attribute\Option;
|
||||
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 Throwable;
|
||||
|
||||
@@ -19,22 +19,9 @@ use function Shlinkio\Shlink\Common\buildDateRange;
|
||||
use function Shlinkio\Shlink\Core\dateRangeToHumanFriendly;
|
||||
use function sprintf;
|
||||
|
||||
class MatomoSendVisitsCommand extends Command implements VisitSendingProgressTrackerInterface
|
||||
{
|
||||
public const string NAME = 'integration:matomo:send-visits';
|
||||
|
||||
private readonly bool $matomoEnabled;
|
||||
private SymfonyStyle $io;
|
||||
|
||||
public function __construct(MatomoOptions $matomoOptions, private readonly MatomoVisitSenderInterface $visitSender)
|
||||
{
|
||||
$this->matomoEnabled = $matomoOptions->enabled;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$help = <<<HELP
|
||||
#[AsCommand(
|
||||
name: MatomoSendVisitsCommand::NAME,
|
||||
help: <<<HELP
|
||||
This command allows you to send existing visits from this Shlink instance to the configured Matomo server.
|
||||
|
||||
Its intention is to allow you to configure Matomo at some point in time, and still have your whole visits
|
||||
@@ -54,32 +41,38 @@ class MatomoSendVisitsCommand extends Command implements VisitSendingProgressTra
|
||||
|
||||
Send all visits created during 2022:
|
||||
<info>%command.name% --since 2022-01-01 --until 2022-12-31</info>
|
||||
HELP;
|
||||
HELP,
|
||||
)]
|
||||
class MatomoSendVisitsCommand extends Command implements VisitSendingProgressTrackerInterface
|
||||
{
|
||||
public const string NAME = 'integration:matomo:send-visits';
|
||||
|
||||
$this
|
||||
->setName(self::NAME)
|
||||
->setDescription(sprintf(
|
||||
'%sSend existing visits to the configured matomo instance',
|
||||
$this->matomoEnabled ? '' : '[MATOMO INTEGRATION DISABLED] ',
|
||||
))
|
||||
->setHelp($help)
|
||||
->addOption(
|
||||
'since',
|
||||
's',
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Only visits created since this date, inclusively, will be sent to Matomo',
|
||||
)
|
||||
->addOption(
|
||||
'until',
|
||||
'u',
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Only visits created until this date, inclusively, will be sent to Matomo',
|
||||
);
|
||||
private readonly bool $matomoEnabled;
|
||||
private SymfonyStyle $io;
|
||||
|
||||
public function __construct(MatomoOptions $matomoOptions, private readonly MatomoVisitSenderInterface $visitSender)
|
||||
{
|
||||
$this->matomoEnabled = $matomoOptions->enabled;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->io = new SymfonyStyle($input, $output);
|
||||
$this->setDescription(sprintf(
|
||||
'%sSend existing visits to the configured matomo instance',
|
||||
$this->matomoEnabled ? '' : '<comment>[MATOMO INTEGRATION DISABLED]</comment> ',
|
||||
));
|
||||
}
|
||||
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
InputInterface $input,
|
||||
#[Option('Only visits created since this date, inclusively, will be sent to Matomo', shortcut: 's')]
|
||||
string|null $since = null,
|
||||
#[Option('Only visits created until this date, inclusively, will be sent to Matomo', shortcut: 'u')]
|
||||
string|null $until = null,
|
||||
): int {
|
||||
$this->io = $io;
|
||||
|
||||
if (! $this->matomoEnabled) {
|
||||
$this->io->warning('Matomo integration is not enabled in this Shlink instance');
|
||||
@@ -87,8 +80,6 @@ class MatomoSendVisitsCommand extends Command implements VisitSendingProgressTra
|
||||
}
|
||||
|
||||
// TODO Validate provided date formats
|
||||
$since = $input->getOption('since');
|
||||
$until = $input->getOption('until');
|
||||
$dateRange = buildDateRange(
|
||||
startDate: $since !== null ? Chronos::parse($since) : null,
|
||||
endDate: $until !== null ? Chronos::parse($until) : null,
|
||||
|
||||
@@ -6,14 +6,18 @@ namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
|
||||
|
||||
use Shlinkio\Shlink\Core\ShortUrl\DeleteShortUrlServiceInterface;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ExpiredShortUrlsConditions;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Attribute\Option;
|
||||
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: DeleteExpiredShortUrlsCommand::NAME,
|
||||
description: 'Deletes all short URLs that are considered expired, because they have a validUntil date in the past',
|
||||
)]
|
||||
class DeleteExpiredShortUrlsCommand extends Command
|
||||
{
|
||||
public const string NAME = 'short-url:delete-expired';
|
||||
@@ -23,32 +27,17 @@ class DeleteExpiredShortUrlsCommand extends Command
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName(self::NAME)
|
||||
->setDescription(
|
||||
'Deletes all short URLs that are considered expired, because they have a validUntil date in the past',
|
||||
)
|
||||
->addOption(
|
||||
'evaluate-max-visits',
|
||||
mode: InputOption::VALUE_NONE,
|
||||
description: 'Also take into consideration short URLs which have reached their max amount of visits.',
|
||||
)
|
||||
->addOption('force', 'f', InputOption::VALUE_NONE, 'Delete short URLs with no confirmation')
|
||||
->addOption(
|
||||
'dry-run',
|
||||
mode: InputOption::VALUE_NONE,
|
||||
description: 'Delete short URLs with no confirmation',
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$force = $input->getOption('force') || ! $input->isInteractive();
|
||||
$dryRun = $input->getOption('dry-run');
|
||||
$conditions = new ExpiredShortUrlsConditions(maxVisitsReached: $input->getOption('evaluate-max-visits'));
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
InputInterface $input,
|
||||
#[Option('Also take into consideration short URLs which have reached their max amount of visits.')]
|
||||
bool $evaluateMaxVisits = false,
|
||||
#[Option('Delete short URLs with no confirmation', shortcut: 'f')] bool $force = false,
|
||||
#[Option('Only check how many short URLs would be affected, without actually deleting them')]
|
||||
bool $dryRun = false,
|
||||
): int {
|
||||
$conditions = new ExpiredShortUrlsConditions(maxVisitsReached: $evaluateMaxVisits);
|
||||
$force = $force || ! $input->isInteractive();
|
||||
|
||||
if (! $force && ! $dryRun) {
|
||||
$io->warning([
|
||||
@@ -69,6 +58,7 @@ class DeleteExpiredShortUrlsCommand extends Command
|
||||
|
||||
$result = $this->deleteShortUrlService->deleteExpiredShortUrls($conditions);
|
||||
$io->success(sprintf('%s expired short URLs have been deleted', $result));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ declare(strict_types=1);
|
||||
namespace Shlinkio\Shlink\CLI\Command\Tag;
|
||||
|
||||
use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Attribute\Option;
|
||||
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;
|
||||
|
||||
#[AsCommand(name: DeleteTagsCommand::NAME, description: 'Deletes one or more tags.')]
|
||||
class DeleteTagsCommand extends Command
|
||||
{
|
||||
public const string NAME = 'tag:delete';
|
||||
@@ -20,24 +20,13 @@ class DeleteTagsCommand extends Command
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName(self::NAME)
|
||||
->setDescription('Deletes one or more tags.')
|
||||
->addOption(
|
||||
'name',
|
||||
't',
|
||||
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
|
||||
'The name of the tags to delete',
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$tagNames = $input->getOption('name');
|
||||
|
||||
/**
|
||||
* @param string[] $tagNames
|
||||
*/
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
#[Option('The name of the tags to delete', name: 'name', shortcut: 't')] array $tagNames = [],
|
||||
): int {
|
||||
if (empty($tagNames)) {
|
||||
$io->warning('You have to provide at least one tag name');
|
||||
return self::INVALID;
|
||||
@@ -45,6 +34,7 @@ class DeleteTagsCommand extends Command
|
||||
|
||||
$this->tagService->deleteTags($tagNames);
|
||||
$io->success('Tags properly deleted');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,13 @@ use Shlinkio\Shlink\CLI\Util\ShlinkTable;
|
||||
use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
|
||||
use Shlinkio\Shlink\Core\Tag\Model\TagsParams;
|
||||
use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
use function array_map;
|
||||
|
||||
#[AsCommand(ListTagsCommand::NAME, 'Lists existing tags.')]
|
||||
class ListTagsCommand extends Command
|
||||
{
|
||||
public const string NAME = 'tag:list';
|
||||
@@ -23,16 +24,9 @@ class ListTagsCommand extends Command
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
public function __invoke(SymfonyStyle $io): int
|
||||
{
|
||||
$this
|
||||
->setName(self::NAME)
|
||||
->setDescription('Lists existing tags.');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
ShlinkTable::default($output)->render(['Name', 'URLs amount', 'Visits amount'], $this->getTagsRows());
|
||||
ShlinkTable::default($io)->render(['Name', 'URLs amount', 'Visits amount'], $this->getTagsRows());
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ use Shlinkio\Shlink\Core\Exception\TagConflictException;
|
||||
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
|
||||
use Shlinkio\Shlink\Core\Model\Renaming;
|
||||
use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
|
||||
use Symfony\Component\Console\Attribute\Argument;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(RenameTagCommand::NAME, 'Renames one existing tag.')]
|
||||
class RenameTagCommand extends Command
|
||||
{
|
||||
public const string NAME = 'tag:rename';
|
||||
@@ -23,21 +23,11 @@ class RenameTagCommand extends Command
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName(self::NAME)
|
||||
->setDescription('Renames one existing tag.')
|
||||
->addArgument('oldName', InputArgument::REQUIRED, 'Current name of the tag.')
|
||||
->addArgument('newName', InputArgument::REQUIRED, 'New name of the tag.');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$oldName = $input->getArgument('oldName');
|
||||
$newName = $input->getArgument('newName');
|
||||
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
#[Argument('Current name of the tag.')] string $oldName,
|
||||
#[Argument('New name of the tag.')] string $newName,
|
||||
): int {
|
||||
try {
|
||||
$this->tagService->renameTag(Renaming::fromNames($oldName, $newName));
|
||||
$io->success('Tag properly renamed.');
|
||||
|
||||
@@ -8,14 +8,17 @@ use Shlinkio\Shlink\Core\Exception\GeolocationDbUpdateFailedException;
|
||||
use Shlinkio\Shlink\Core\Geolocation\GeolocationDbUpdaterInterface;
|
||||
use Shlinkio\Shlink\Core\Geolocation\GeolocationDownloadProgressHandlerInterface;
|
||||
use Shlinkio\Shlink\Core\Geolocation\GeolocationResult;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
#[AsCommand(
|
||||
DownloadGeoLiteDbCommand::NAME,
|
||||
'Checks if the GeoLite2 db file is too old or it does not exist, and tries to download an up-to-date copy if so.',
|
||||
)]
|
||||
class DownloadGeoLiteDbCommand extends Command implements GeolocationDownloadProgressHandlerInterface
|
||||
{
|
||||
public const string NAME = 'visit:download-db';
|
||||
@@ -28,19 +31,9 @@ class DownloadGeoLiteDbCommand extends Command implements GeolocationDownloadPro
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
public function __invoke(SymfonyStyle $io): int
|
||||
{
|
||||
$this
|
||||
->setName(self::NAME)
|
||||
->setDescription(
|
||||
'Checks if the GeoLite2 db file is too old or it does not exist, and tries to download an up-to-date '
|
||||
. 'copy if so.',
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->io = new SymfonyStyle($input, $output);
|
||||
$this->io = $io;
|
||||
|
||||
try {
|
||||
$result = $this->dbUpdater->checkDbUpdate($this);
|
||||
|
||||
@@ -35,7 +35,7 @@ class InitialApiKeyCommandTest extends TestCase
|
||||
$this->apiKeyService->expects($this->once())->method('createInitial')->with('the_key')->willReturn($result);
|
||||
|
||||
$this->commandTester->execute(
|
||||
['apiKey' => 'the_key'],
|
||||
['api-key' => 'the_key'],
|
||||
['verbosity' => $verbose ? OutputInterface::VERBOSITY_VERBOSE : OutputInterface::VERBOSITY_NORMAL],
|
||||
);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
|
||||
@@ -28,13 +28,13 @@ class ReadEnvVarCommandTest extends TestCase
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('foo is not a valid Shlink environment variable');
|
||||
|
||||
$this->commandTester->execute(['envVar' => 'foo']);
|
||||
$this->commandTester->execute(['env-var' => 'foo']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function valueIsPrintedIfProvidedEnvVarIsValid(): void
|
||||
{
|
||||
$this->commandTester->execute(['envVar' => EnvVars::BASE_PATH->value]);
|
||||
$this->commandTester->execute(['env-var' => EnvVars::BASE_PATH->value]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
|
||||
self::assertStringNotContainsString('Select the env var to read', $output);
|
||||
|
||||
@@ -36,8 +36,8 @@ class RenameTagCommandTest extends TestCase
|
||||
)->willThrowException(TagNotFoundException::fromTag('foo'));
|
||||
|
||||
$this->commandTester->execute([
|
||||
'oldName' => $oldName,
|
||||
'newName' => $newName,
|
||||
'old-name' => $oldName,
|
||||
'new-name' => $newName,
|
||||
]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
|
||||
@@ -54,8 +54,8 @@ class RenameTagCommandTest extends TestCase
|
||||
)->willReturn(new Tag($newName));
|
||||
|
||||
$this->commandTester->execute([
|
||||
'oldName' => $oldName,
|
||||
'newName' => $newName,
|
||||
'old-name' => $oldName,
|
||||
'new-name' => $newName,
|
||||
]);
|
||||
$output = $this->commandTester->getDisplay();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user