2026-08-18 16:56:15 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace Presentation\Console\Commands;
|
|
|
|
|
|
|
|
|
|
use Application\Security\Exceptions\ClientNotFoundException;
|
|
|
|
|
use Application\Security\Models\Entities\Client;
|
|
|
|
|
use Application\Security\Models\ValueObjects\ClientIdentifier;
|
|
|
|
|
use Application\Security\Usecases\ToggleClientStatus\Input;
|
|
|
|
|
use Application\Security\Usecases\ToggleClientStatus\SetClientStatus;
|
|
|
|
|
use Illuminate\Console\Command;
|
|
|
|
|
use Presentation\Console\Presenters\Clients\SetClientStatusPresenter;
|
2026-08-18 18:25:08 +02:00
|
|
|
use Presentation\Console\Services\ClientConsoleResolver;
|
2026-08-18 16:56:15 +02:00
|
|
|
use Shared\DDD\Usecase\Builder\UsecaseBuilder;
|
|
|
|
|
use Shared\DDD\Usecase\Builder\UsecaseExecutor;
|
|
|
|
|
use Symfony\Component\Console\Completion\CompletionInput;
|
|
|
|
|
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
|
|
|
|
use Symfony\Component\Console\Completion\Suggestion;
|
|
|
|
|
|
|
|
|
|
final class ToggleClientStatusCommand extends Command
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* @var string
|
|
|
|
|
*/
|
|
|
|
|
protected $signature = 'client:toggle-status
|
|
|
|
|
{identifier? : The identifier of the client}
|
|
|
|
|
{status? : The desired status of the client (activate|deactivate)}';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @var string
|
|
|
|
|
*/
|
|
|
|
|
protected $description = 'Toggle the status of an API client';
|
|
|
|
|
|
|
|
|
|
public function __construct(
|
2026-08-18 18:25:08 +02:00
|
|
|
private readonly ClientConsoleResolver $clientResolver,
|
2026-08-18 16:56:15 +02:00
|
|
|
) {
|
|
|
|
|
parent::__construct();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[\Override]
|
|
|
|
|
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
|
|
|
|
{
|
|
|
|
|
parent::complete($input, $suggestions);
|
|
|
|
|
|
|
|
|
|
if ($input->mustSuggestArgumentValuesFor('identifier')) {
|
2026-08-18 18:25:08 +02:00
|
|
|
$this->clientResolver->complete($suggestions);
|
2026-08-18 16:56:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($input->mustSuggestArgumentValuesFor('status')) {
|
|
|
|
|
$suggestions->suggestValues([
|
|
|
|
|
new Suggestion('activate', 'Activate the client'),
|
|
|
|
|
new Suggestion('deactivate', 'Deactivate the client'),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function handle(
|
|
|
|
|
SetClientStatus $usecase,
|
|
|
|
|
UsecaseExecutor $executor,
|
|
|
|
|
): int {
|
2026-08-18 18:26:46 +02:00
|
|
|
$input = $this->getInput();
|
|
|
|
|
|
|
|
|
|
if ($input === null) {
|
|
|
|
|
return self::FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$view = UsecaseBuilder::using($executor)
|
|
|
|
|
->do($usecase)
|
|
|
|
|
->from($input)
|
|
|
|
|
->with(new SetClientStatusPresenter())
|
|
|
|
|
->run();
|
|
|
|
|
|
|
|
|
|
$this->info($view->message);
|
|
|
|
|
|
|
|
|
|
return self::SUCCESS;
|
|
|
|
|
} catch (ClientNotFoundException | \InvalidArgumentException $e) {
|
|
|
|
|
$this->error($e->getMessage());
|
|
|
|
|
|
|
|
|
|
return self::FAILURE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function getInput(): ?Input
|
|
|
|
|
{
|
|
|
|
|
$client = $this->resolveClient();
|
|
|
|
|
|
|
|
|
|
if ($client === null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$desiredStatus = $this->resolveDesiredStatus($client);
|
|
|
|
|
|
|
|
|
|
return new Input($client->identifier, $desiredStatus);
|
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
|
$this->error($e->getMessage());
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function resolveClient(): ?Client
|
|
|
|
|
{
|
2026-08-18 18:25:08 +02:00
|
|
|
$raw = $this->argument('identifier');
|
|
|
|
|
$rawString = is_string($raw) ? $raw : $this->anticipate(
|
|
|
|
|
'Search client (by name, ID, identifier, or slug):',
|
|
|
|
|
fn (string $input): array => $this->clientResolver->searchSuggestions($input),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (! is_string($rawString) || trim($rawString) === '') {
|
|
|
|
|
$this->error('No client specified.');
|
|
|
|
|
|
2026-08-18 18:26:46 +02:00
|
|
|
return null;
|
2026-08-18 18:25:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$inputString = trim($rawString);
|
|
|
|
|
$client = $this->clientResolver->find($inputString);
|
|
|
|
|
|
|
|
|
|
if ($client === null) {
|
|
|
|
|
try {
|
|
|
|
|
new ClientIdentifier($inputString);
|
|
|
|
|
$this->error("Client '{$inputString}' not found.");
|
|
|
|
|
} catch (\InvalidArgumentException) {
|
|
|
|
|
$this->error("Invalid client identifier '{$inputString}'. Expected a valid client identifier.");
|
|
|
|
|
}
|
2026-08-18 16:56:15 +02:00
|
|
|
|
2026-08-18 18:26:46 +02:00
|
|
|
return null;
|
2026-08-18 16:56:15 +02:00
|
|
|
}
|
|
|
|
|
|
2026-08-18 18:26:46 +02:00
|
|
|
return $client;
|
2026-08-18 16:56:15 +02:00
|
|
|
}
|
|
|
|
|
|
2026-08-18 18:25:08 +02:00
|
|
|
private function resolveDesiredStatus(Client $client): bool
|
2026-08-18 16:56:15 +02:00
|
|
|
{
|
|
|
|
|
$status = $this->argument('status') !== null ? (string) $this->argument('status') : null;
|
|
|
|
|
|
|
|
|
|
return match ($status) {
|
|
|
|
|
'activate' => true,
|
|
|
|
|
'deactivate' => false,
|
2026-08-18 18:25:08 +02:00
|
|
|
null => ! $client->isActive,
|
2026-08-18 16:56:15 +02:00
|
|
|
default => throw new \InvalidArgumentException("Invalid status '{$status}'. Expected 'activate' or 'deactivate'."),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|