diff --git a/module/CLI/src/Command/Api/GenerateKeyCommand.php b/module/CLI/src/Command/Api/GenerateKeyCommand.php index 23f0f8fc..37f0aaca 100644 --- a/module/CLI/src/Command/Api/GenerateKeyCommand.php +++ b/module/CLI/src/Command/Api/GenerateKeyCommand.php @@ -7,6 +7,7 @@ namespace Shlinkio\Shlink\CLI\Command\Api; use Cake\Chronos\Chronos; use Shlinkio\Shlink\CLI\ApiKey\RoleResolverInterface; use Shlinkio\Shlink\CLI\Util\ExitCodes; +use Shlinkio\Shlink\CLI\Util\ShlinkTable; use Shlinkio\Shlink\Rest\ApiKey\Role; use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface; use Symfony\Component\Console\Command\Command; @@ -15,6 +16,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use function Shlinkio\Shlink\Core\arrayToString; use function sprintf; class GenerateKeyCommand extends Command @@ -81,7 +83,17 @@ class GenerateKeyCommand extends Command ); // TODO Print permissions that have been set - (new SymfonyStyle($input, $output))->success(sprintf('Generated API key: "%s"', $apiKey->toString())); + $io = new SymfonyStyle($input, $output); + $io->success(sprintf('Generated API key: "%s"', $apiKey->toString())); + + if (! $apiKey->isAdmin()) { + ShlinkTable::fromOutput($io)->render( + ['Role name', 'Role metadata'], + $apiKey->mapRoles(fn (string $name, array $meta) => [$name, arrayToString($meta, 0)]), + null, + 'Roles', + ); + } return ExitCodes::EXIT_SUCCESS; } diff --git a/module/Core/functions/functions.php b/module/Core/functions/functions.php index c766b767..076de6a0 100644 --- a/module/Core/functions/functions.php +++ b/module/Core/functions/functions.php @@ -14,6 +14,7 @@ use function Functional\reduce_left; use function is_array; use function print_r; use function sprintf; +use function str_repeat; const DEFAULT_DELETE_SHORT_URL_THRESHOLD = 15; const DEFAULT_SHORT_CODES_LENGTH = 5; @@ -79,11 +80,20 @@ function getOptionalBoolFromInputFilter(InputFilter $inputFilter, string $fieldN return $value !== null ? (bool) $value : null; } -function arrayToString(array $array): string +function arrayToString(array $array, int $indentSize = 4): string { - return reduce_left($array, fn ($messages, string $name, $_, string $acc) => $acc . sprintf( - "\n '%s' => %s", - $name, - is_array($messages) ? print_r($messages, true) : $messages, - ), ''); + $indent = str_repeat(' ', $indentSize); + $index = 0; + + return reduce_left($array, static function ($messages, string $name, $_, string $acc) use (&$index, $indent) { + $index++; + + return $acc . sprintf( + "%s%s'%s' => %s", + $index === 1 ? '' : "\n", + $indent, + $name, + is_array($messages) ? print_r($messages, true) : $messages, + ); + }, ''); } diff --git a/module/Core/src/Exception/ValidationException.php b/module/Core/src/Exception/ValidationException.php index 195cfa42..3a211592 100644 --- a/module/Core/src/Exception/ValidationException.php +++ b/module/Core/src/Exception/ValidationException.php @@ -53,11 +53,12 @@ class ValidationException extends InvalidArgumentException implements ProblemDet public function __toString(): string { return sprintf( - '%s %s in %s:%s%s%sStack trace:%s%s', + '%s %s in %s:%s%s%s%sStack trace:%s%s', __CLASS__, $this->getMessage(), $this->getFile(), $this->getLine(), + PHP_EOL, arrayToString($this->getInvalidElements()), PHP_EOL, PHP_EOL, diff --git a/module/Core/test/Exception/ValidationExceptionTest.php b/module/Core/test/Exception/ValidationExceptionTest.php index fab70760..a0980738 100644 --- a/module/Core/test/Exception/ValidationExceptionTest.php +++ b/module/Core/test/Exception/ValidationExceptionTest.php @@ -39,7 +39,7 @@ class ValidationExceptionTest extends TestCase $inputFilter = $this->prophesize(InputFilterInterface::class); $getMessages = $inputFilter->getMessages()->willReturn($invalidData); - $e = ValidationException::fromInputFilter($inputFilter->reveal()); + $e = ValidationException::fromInputFilter($inputFilter->reveal(), $prev); self::assertEquals($invalidData, $e->getInvalidElements()); self::assertEquals(['invalidElements' => array_keys($invalidData)], $e->getAdditionalData()); @@ -52,6 +52,6 @@ class ValidationExceptionTest extends TestCase public function provideExceptions(): iterable { - return [[null, new RuntimeException(), new LogicException()]]; + return [[null], [new RuntimeException()], [new LogicException()]]; } } diff --git a/module/Rest/src/Entity/ApiKey.php b/module/Rest/src/Entity/ApiKey.php index 4538829c..62729031 100644 --- a/module/Rest/src/Entity/ApiKey.php +++ b/module/Rest/src/Entity/ApiKey.php @@ -115,6 +115,11 @@ class ApiKey extends AbstractEntity return $role === null ? [] : $role->meta(); } + public function mapRoles(callable $fun): array + { + return $this->roles->map(fn (ApiKeyRole $role) => $fun($role->name(), $role->meta()))->getValues(); + } + public function registerRole(RoleDefinition $roleDefinition): void { $roleName = $roleDefinition->roleName();