2026-08-18 17:12:47 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace Presentation\Console\Commands;
|
|
|
|
|
|
2026-08-18 17:31:29 +02:00
|
|
|
use Application\Security\Exceptions\ClientNameAlreadyExistsException;
|
2026-08-18 17:12:47 +02:00
|
|
|
use Application\Security\Models\ValueObjects\ClientName;
|
2026-08-18 17:31:29 +02:00
|
|
|
use Application\Security\Ports\Repositories\ClientRepository;
|
2026-08-18 17:12:47 +02:00
|
|
|
use Application\Security\Usecases\CreateClient\CreateClient;
|
|
|
|
|
use Application\Security\Usecases\CreateClient\Input;
|
|
|
|
|
use Illuminate\Console\Command;
|
|
|
|
|
use Presentation\Console\Presenters\Clients\CreateClientPresenter;
|
|
|
|
|
use Presentation\Console\Presenters\Clients\CreateClientView;
|
|
|
|
|
use Shared\DDD\Usecase\Builder\UsecaseBuilder;
|
|
|
|
|
use Shared\DDD\Usecase\Builder\UsecaseExecutor;
|
|
|
|
|
|
2026-08-18 18:46:31 +02:00
|
|
|
use function Laravel\Prompts\confirm;
|
|
|
|
|
use function Laravel\Prompts\text;
|
|
|
|
|
|
2026-08-18 17:12:47 +02:00
|
|
|
final class CreateClientCommand extends Command
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* @var string
|
|
|
|
|
*/
|
2026-08-20 17:35:31 +02:00
|
|
|
protected $signature = 'arcadia:client:create';
|
2026-08-18 17:12:47 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @var string
|
|
|
|
|
*/
|
|
|
|
|
protected $description = 'Create a new API client (interactive wizard)';
|
|
|
|
|
|
2026-08-18 17:31:29 +02:00
|
|
|
public function __construct(
|
|
|
|
|
private readonly ClientRepository $clientRepository,
|
|
|
|
|
) {
|
|
|
|
|
parent::__construct();
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-18 17:12:47 +02:00
|
|
|
public function handle(
|
|
|
|
|
CreateClient $usecase,
|
|
|
|
|
UsecaseExecutor $executor,
|
|
|
|
|
): int {
|
|
|
|
|
$clientName = $this->askForClientName();
|
|
|
|
|
|
|
|
|
|
if (! $this->confirmCreation($clientName)) {
|
|
|
|
|
$this->warn('Client creation cancelled.');
|
|
|
|
|
|
|
|
|
|
return self::SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$view = UsecaseBuilder::using($executor)
|
|
|
|
|
->do($usecase)
|
2026-08-18 18:26:46 +02:00
|
|
|
->from($this->getInput($clientName))
|
2026-08-18 17:12:47 +02:00
|
|
|
->with(new CreateClientPresenter())
|
|
|
|
|
->run();
|
|
|
|
|
|
|
|
|
|
$this->displayCredentials($view);
|
|
|
|
|
|
|
|
|
|
return self::SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-18 18:26:46 +02:00
|
|
|
private function getInput(ClientName $clientName): Input
|
|
|
|
|
{
|
|
|
|
|
return new Input($clientName);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-18 18:46:31 +02:00
|
|
|
private function askForClientName(): ClientName
|
2026-08-18 17:12:47 +02:00
|
|
|
{
|
2026-08-18 18:46:31 +02:00
|
|
|
$rawName = text(
|
|
|
|
|
label: 'Client name (e.g. "Ones Branding website")',
|
|
|
|
|
placeholder: 'E.g. Ones Branding website',
|
|
|
|
|
required: 'Client name cannot be empty.',
|
|
|
|
|
validate: function (string $value): ?string {
|
|
|
|
|
$trimmed = trim($value);
|
|
|
|
|
|
|
|
|
|
if ($trimmed === '') {
|
|
|
|
|
return 'Client name cannot be empty.';
|
2026-08-18 17:31:29 +02:00
|
|
|
}
|
|
|
|
|
|
2026-08-18 18:46:31 +02:00
|
|
|
try {
|
|
|
|
|
$clientName = new ClientName($trimmed);
|
2026-08-18 17:12:47 +02:00
|
|
|
|
2026-08-18 18:46:31 +02:00
|
|
|
if ($this->clientRepository->existsByName($clientName)) {
|
2026-08-20 14:30:09 +02:00
|
|
|
return ClientNameAlreadyExistsException::forName($clientName)->getMessage();
|
2026-08-18 18:46:31 +02:00
|
|
|
}
|
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
|
return $e->getMessage();
|
2026-08-18 17:12:47 +02:00
|
|
|
}
|
|
|
|
|
|
2026-08-18 18:46:31 +02:00
|
|
|
return null;
|
|
|
|
|
},
|
|
|
|
|
);
|
2026-08-18 17:12:47 +02:00
|
|
|
|
2026-08-18 18:46:31 +02:00
|
|
|
return new ClientName(trim($rawName));
|
2026-08-18 17:12:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function confirmCreation(ClientName $name): bool
|
|
|
|
|
{
|
|
|
|
|
$this->newLine();
|
|
|
|
|
$this->info('Summary:');
|
|
|
|
|
$this->table(
|
|
|
|
|
['Field', 'Value'],
|
|
|
|
|
[
|
|
|
|
|
['Name', $name->value],
|
|
|
|
|
['Identifier', '<auto-generated>'],
|
|
|
|
|
['Secret', '<auto-generated>'],
|
|
|
|
|
['Status', 'Active'],
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
2026-08-18 18:46:31 +02:00
|
|
|
return confirm(
|
|
|
|
|
label: 'Create this API client?',
|
|
|
|
|
default: false,
|
|
|
|
|
);
|
2026-08-18 17:12:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function displayCredentials(CreateClientView $view): void
|
|
|
|
|
{
|
|
|
|
|
$this->newLine();
|
|
|
|
|
$this->info($view->message);
|
|
|
|
|
$this->newLine();
|
|
|
|
|
|
|
|
|
|
$this->table(
|
|
|
|
|
['Field', 'Value'],
|
|
|
|
|
[
|
|
|
|
|
['ID', $view->id],
|
|
|
|
|
['Name', $view->name],
|
|
|
|
|
['Identifier', $view->identifier],
|
|
|
|
|
['Secret', $view->secret],
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->newLine();
|
|
|
|
|
$this->warn('WARNING: Store the secret securely, it cannot be retrieved later.');
|
|
|
|
|
}
|
|
|
|
|
}
|