From a41835573b959357724c9d43558f5ee47ca82e1c Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 14 Aug 2022 13:12:10 +0200 Subject: [PATCH] Centralized prefix for problem detail types --- config/autoload/error-handler.global.php | 6 ++-- module/Core/functions/functions.php | 5 ++++ .../src/Exception/DeleteShortUrlException.php | 5 ++-- .../src/Exception/DomainNotFoundException.php | 5 ++-- .../ForbiddenTagOperationException.php | 6 ++-- .../src/Exception/InvalidUrlException.php | 5 ++-- .../src/Exception/NonUniqueSlugException.php | 5 ++-- .../Exception/ShortUrlNotFoundException.php | 5 ++-- .../src/Exception/TagConflictException.php | 5 ++-- .../src/Exception/TagNotFoundException.php | 5 ++-- .../src/Exception/ValidationException.php | 5 ++-- ...wardsCompatibleProblemDetailsException.php | 30 +++++++++++-------- .../Rest/src/Exception/MercureException.php | 6 ++-- .../MissingAuthenticationException.php | 5 ++-- .../VerifyAuthenticationException.php | 6 ++-- ...sCompatibleProblemDetailsExceptionTest.php | 24 +++++++-------- 16 files changed, 77 insertions(+), 51 deletions(-) diff --git a/config/autoload/error-handler.global.php b/config/autoload/error-handler.global.php index 2cf28a47..65e5b616 100644 --- a/config/autoload/error-handler.global.php +++ b/config/autoload/error-handler.global.php @@ -6,12 +6,14 @@ use Laminas\Stratigility\Middleware\ErrorHandler; use Mezzio\ProblemDetails\ProblemDetailsMiddleware; use Shlinkio\Shlink\Common\Logger; +use function Shlinkio\Shlink\Core\toProblemDetailsType; + return [ 'problem-details' => [ 'default_types_map' => [ - 404 => 'NOT_FOUND', // TODO Define new values, with backwards compatibility if possible - 500 => 'INTERNAL_SERVER_ERROR', // TODO Define new values, with backwards compatibility if possible + 404 => toProblemDetailsType('not-found'), + 500 => toProblemDetailsType('internal-server-error'), ], ], diff --git a/module/Core/functions/functions.php b/module/Core/functions/functions.php index c5186e41..d34175c7 100644 --- a/module/Core/functions/functions.php +++ b/module/Core/functions/functions.php @@ -127,3 +127,8 @@ function camelCaseToHumanFriendly(string $value): string return ucfirst($filter->filter($value)); } + +function toProblemDetailsType(string $errorCode): string +{ + return sprintf('https://shlink.io/api/error/%s', $errorCode); +} diff --git a/module/Core/src/Exception/DeleteShortUrlException.php b/module/Core/src/Exception/DeleteShortUrlException.php index f6638221..f8a5cfa8 100644 --- a/module/Core/src/Exception/DeleteShortUrlException.php +++ b/module/Core/src/Exception/DeleteShortUrlException.php @@ -9,6 +9,7 @@ use Mezzio\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait; use Mezzio\ProblemDetails\Exception\ProblemDetailsExceptionInterface; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; +use function Shlinkio\Shlink\Core\toProblemDetailsType; use function sprintf; class DeleteShortUrlException extends DomainException implements ProblemDetailsExceptionInterface @@ -16,7 +17,7 @@ class DeleteShortUrlException extends DomainException implements ProblemDetailsE use CommonProblemDetailsExceptionTrait; private const TITLE = 'Cannot delete short URL'; - public const TYPE = 'https://shlink.io/api/error/invalid-short-url-deletion'; + public const ERROR_CODE = 'invalid-short-url-deletion'; public static function fromVisitsThreshold(int $threshold, ShortUrlIdentifier $identifier): self { @@ -32,7 +33,7 @@ class DeleteShortUrlException extends DomainException implements ProblemDetailsE $e->detail = $e->getMessage(); $e->title = self::TITLE; - $e->type = self::TYPE; + $e->type = toProblemDetailsType(self::ERROR_CODE); $e->status = StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY; $e->additional = [ 'shortCode' => $shortCode, diff --git a/module/Core/src/Exception/DomainNotFoundException.php b/module/Core/src/Exception/DomainNotFoundException.php index aca67813..688a4edc 100644 --- a/module/Core/src/Exception/DomainNotFoundException.php +++ b/module/Core/src/Exception/DomainNotFoundException.php @@ -8,6 +8,7 @@ use Fig\Http\Message\StatusCodeInterface; use Mezzio\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait; use Mezzio\ProblemDetails\Exception\ProblemDetailsExceptionInterface; +use function Shlinkio\Shlink\Core\toProblemDetailsType; use function sprintf; class DomainNotFoundException extends DomainException implements ProblemDetailsExceptionInterface @@ -15,7 +16,7 @@ class DomainNotFoundException extends DomainException implements ProblemDetailsE use CommonProblemDetailsExceptionTrait; private const TITLE = 'Domain not found'; - public const TYPE = 'https://shlink.io/api/error/domain-not-found'; + public const ERROR_CODE = 'domain-not-found'; private function __construct(string $message, array $additional) { @@ -23,7 +24,7 @@ class DomainNotFoundException extends DomainException implements ProblemDetailsE $this->detail = $message; $this->title = self::TITLE; - $this->type = self::TYPE; + $this->type = toProblemDetailsType(self::ERROR_CODE); $this->status = StatusCodeInterface::STATUS_NOT_FOUND; $this->additional = $additional; } diff --git a/module/Core/src/Exception/ForbiddenTagOperationException.php b/module/Core/src/Exception/ForbiddenTagOperationException.php index 6da1cb61..64ae156c 100644 --- a/module/Core/src/Exception/ForbiddenTagOperationException.php +++ b/module/Core/src/Exception/ForbiddenTagOperationException.php @@ -8,12 +8,14 @@ use Fig\Http\Message\StatusCodeInterface; use Mezzio\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait; use Mezzio\ProblemDetails\Exception\ProblemDetailsExceptionInterface; +use function Shlinkio\Shlink\Core\toProblemDetailsType; + class ForbiddenTagOperationException extends DomainException implements ProblemDetailsExceptionInterface { use CommonProblemDetailsExceptionTrait; private const TITLE = 'Forbidden tag operation'; - public const TYPE = 'https://shlink.io/api/error/forbidden-tag-operation'; + public const ERROR_CODE = 'forbidden-tag-operation'; public static function forDeletion(): self { @@ -31,7 +33,7 @@ class ForbiddenTagOperationException extends DomainException implements ProblemD $e->detail = $message; $e->title = self::TITLE; - $e->type = self::TYPE; + $e->type = toProblemDetailsType(self::ERROR_CODE); $e->status = StatusCodeInterface::STATUS_FORBIDDEN; return $e; diff --git a/module/Core/src/Exception/InvalidUrlException.php b/module/Core/src/Exception/InvalidUrlException.php index 6d1f93c6..200914c2 100644 --- a/module/Core/src/Exception/InvalidUrlException.php +++ b/module/Core/src/Exception/InvalidUrlException.php @@ -9,6 +9,7 @@ use Mezzio\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait; use Mezzio\ProblemDetails\Exception\ProblemDetailsExceptionInterface; use Throwable; +use function Shlinkio\Shlink\Core\toProblemDetailsType; use function sprintf; class InvalidUrlException extends DomainException implements ProblemDetailsExceptionInterface @@ -16,7 +17,7 @@ class InvalidUrlException extends DomainException implements ProblemDetailsExcep use CommonProblemDetailsExceptionTrait; private const TITLE = 'Invalid URL'; - public const TYPE = 'https://shlink.io/api/error/invalid-url'; + public const ERROR_CODE = 'invalid-url'; public static function fromUrl(string $url, ?Throwable $previous = null): self { @@ -25,7 +26,7 @@ class InvalidUrlException extends DomainException implements ProblemDetailsExcep $e->detail = $e->getMessage(); $e->title = self::TITLE; - $e->type = self::TYPE; + $e->type = toProblemDetailsType(self::ERROR_CODE); $e->status = $status; $e->additional = ['url' => $url]; diff --git a/module/Core/src/Exception/NonUniqueSlugException.php b/module/Core/src/Exception/NonUniqueSlugException.php index dc1fcca9..5336786c 100644 --- a/module/Core/src/Exception/NonUniqueSlugException.php +++ b/module/Core/src/Exception/NonUniqueSlugException.php @@ -9,6 +9,7 @@ use Mezzio\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait; use Mezzio\ProblemDetails\Exception\ProblemDetailsExceptionInterface; use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl; +use function Shlinkio\Shlink\Core\toProblemDetailsType; use function sprintf; class NonUniqueSlugException extends InvalidArgumentException implements ProblemDetailsExceptionInterface @@ -16,7 +17,7 @@ class NonUniqueSlugException extends InvalidArgumentException implements Problem use CommonProblemDetailsExceptionTrait; private const TITLE = 'Invalid custom slug'; - public const TYPE = 'https://shlink.io/api/error/non-unique-slug'; + public const ERROR_CODE = 'non-unique-slug'; public static function fromSlug(string $slug, ?string $domain = null): self { @@ -25,7 +26,7 @@ class NonUniqueSlugException extends InvalidArgumentException implements Problem $e->detail = $e->getMessage(); $e->title = self::TITLE; - $e->type = self::TYPE; + $e->type = toProblemDetailsType(self::ERROR_CODE); $e->status = StatusCodeInterface::STATUS_BAD_REQUEST; $e->additional = ['customSlug' => $slug]; diff --git a/module/Core/src/Exception/ShortUrlNotFoundException.php b/module/Core/src/Exception/ShortUrlNotFoundException.php index 4da6972e..49b8cc02 100644 --- a/module/Core/src/Exception/ShortUrlNotFoundException.php +++ b/module/Core/src/Exception/ShortUrlNotFoundException.php @@ -9,6 +9,7 @@ use Mezzio\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait; use Mezzio\ProblemDetails\Exception\ProblemDetailsExceptionInterface; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; +use function Shlinkio\Shlink\Core\toProblemDetailsType; use function sprintf; class ShortUrlNotFoundException extends DomainException implements ProblemDetailsExceptionInterface @@ -16,7 +17,7 @@ class ShortUrlNotFoundException extends DomainException implements ProblemDetail use CommonProblemDetailsExceptionTrait; private const TITLE = 'Short URL not found'; - public const TYPE = 'https://shlink.io/api/error/short-url-not-found'; + public const ERROR_CODE = 'short-url-not-found'; public static function fromNotFound(ShortUrlIdentifier $identifier): self { @@ -27,7 +28,7 @@ class ShortUrlNotFoundException extends DomainException implements ProblemDetail $e->detail = $e->getMessage(); $e->title = self::TITLE; - $e->type = self::TYPE; + $e->type = toProblemDetailsType(self::ERROR_CODE); $e->status = StatusCodeInterface::STATUS_NOT_FOUND; $e->additional = ['shortCode' => $shortCode]; diff --git a/module/Core/src/Exception/TagConflictException.php b/module/Core/src/Exception/TagConflictException.php index 09ea7be4..0fc5c317 100644 --- a/module/Core/src/Exception/TagConflictException.php +++ b/module/Core/src/Exception/TagConflictException.php @@ -9,6 +9,7 @@ use Mezzio\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait; use Mezzio\ProblemDetails\Exception\ProblemDetailsExceptionInterface; use Shlinkio\Shlink\Core\Tag\Model\TagRenaming; +use function Shlinkio\Shlink\Core\toProblemDetailsType; use function sprintf; class TagConflictException extends RuntimeException implements ProblemDetailsExceptionInterface @@ -16,7 +17,7 @@ class TagConflictException extends RuntimeException implements ProblemDetailsExc use CommonProblemDetailsExceptionTrait; private const TITLE = 'Tag conflict'; - public const TYPE = 'https://shlink.io/api/error/tag-conflict'; + public const ERROR_CODE = 'tag-conflict'; public static function forExistingTag(TagRenaming $renaming): self { @@ -24,7 +25,7 @@ class TagConflictException extends RuntimeException implements ProblemDetailsExc $e->detail = $e->getMessage(); $e->title = self::TITLE; - $e->type = self::TYPE; + $e->type = toProblemDetailsType(self::ERROR_CODE); $e->status = StatusCodeInterface::STATUS_CONFLICT; $e->additional = $renaming->toArray(); diff --git a/module/Core/src/Exception/TagNotFoundException.php b/module/Core/src/Exception/TagNotFoundException.php index da2426aa..8fdd395a 100644 --- a/module/Core/src/Exception/TagNotFoundException.php +++ b/module/Core/src/Exception/TagNotFoundException.php @@ -8,6 +8,7 @@ use Fig\Http\Message\StatusCodeInterface; use Mezzio\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait; use Mezzio\ProblemDetails\Exception\ProblemDetailsExceptionInterface; +use function Shlinkio\Shlink\Core\toProblemDetailsType; use function sprintf; class TagNotFoundException extends DomainException implements ProblemDetailsExceptionInterface @@ -15,7 +16,7 @@ class TagNotFoundException extends DomainException implements ProblemDetailsExce use CommonProblemDetailsExceptionTrait; private const TITLE = 'Tag not found'; - public const TYPE = 'https://shlink.io/api/error/tag-not-found'; + public const ERROR_CODE = 'tag-not-found'; public static function fromTag(string $tag): self { @@ -23,7 +24,7 @@ class TagNotFoundException extends DomainException implements ProblemDetailsExce $e->detail = $e->getMessage(); $e->title = self::TITLE; - $e->type = self::TYPE; + $e->type = toProblemDetailsType(self::ERROR_CODE); $e->status = StatusCodeInterface::STATUS_NOT_FOUND; $e->additional = ['tag' => $tag]; diff --git a/module/Core/src/Exception/ValidationException.php b/module/Core/src/Exception/ValidationException.php index 0f625bc5..dcb11fa4 100644 --- a/module/Core/src/Exception/ValidationException.php +++ b/module/Core/src/Exception/ValidationException.php @@ -12,6 +12,7 @@ use Throwable; use function array_keys; use function Shlinkio\Shlink\Core\arrayToString; +use function Shlinkio\Shlink\Core\toProblemDetailsType; use function sprintf; use const PHP_EOL; @@ -21,7 +22,7 @@ class ValidationException extends InvalidArgumentException implements ProblemDet use CommonProblemDetailsExceptionTrait; private const TITLE = 'Invalid data'; - public const TYPE = 'https://shlink.io/api/error/invalid-data'; + public const ERROR_CODE = 'invalid-data'; private array $invalidElements; @@ -37,7 +38,7 @@ class ValidationException extends InvalidArgumentException implements ProblemDet $e->detail = $e->getMessage(); $e->title = self::TITLE; - $e->type = self::TYPE; + $e->type = toProblemDetailsType(self::ERROR_CODE); $e->status = StatusCodeInterface::STATUS_BAD_REQUEST; $e->invalidElements = $invalidData; $e->additional = ['invalidElements' => array_keys($invalidData)]; diff --git a/module/Rest/src/Exception/BackwardsCompatibleProblemDetailsException.php b/module/Rest/src/Exception/BackwardsCompatibleProblemDetailsException.php index 14a6e934..685d3795 100644 --- a/module/Rest/src/Exception/BackwardsCompatibleProblemDetailsException.php +++ b/module/Rest/src/Exception/BackwardsCompatibleProblemDetailsException.php @@ -15,6 +15,9 @@ use Shlinkio\Shlink\Core\Exception\TagConflictException; use Shlinkio\Shlink\Core\Exception\TagNotFoundException; use Shlinkio\Shlink\Core\Exception\ValidationException; +use function explode; +use function Functional\last; + /** @deprecated */ class BackwardsCompatibleProblemDetailsException extends RuntimeException implements ProblemDetailsExceptionInterface { @@ -74,19 +77,20 @@ class BackwardsCompatibleProblemDetailsException extends RuntimeException implem private function remapType(string $wrappedType): string { - return match ($wrappedType) { - ValidationException::TYPE => 'INVALID_ARGUMENT', - DeleteShortUrlException::TYPE => 'INVALID_SHORT_URL_DELETION', - DomainNotFoundException::TYPE => 'DOMAIN_NOT_FOUND', - ForbiddenTagOperationException::TYPE => 'FORBIDDEN_OPERATION', - InvalidUrlException::TYPE => 'INVALID_URL', - NonUniqueSlugException::TYPE => 'INVALID_SLUG', - ShortUrlNotFoundException::TYPE => 'INVALID_SHORTCODE', - TagConflictException::TYPE => 'TAG_CONFLICT', - TagNotFoundException::TYPE => 'TAG_NOT_FOUND', - MercureException::TYPE => 'MERCURE_NOT_CONFIGURED', - MissingAuthenticationException::TYPE => 'INVALID_AUTHORIZATION', - VerifyAuthenticationException::TYPE => 'INVALID_API_KEY', + $lastSegment = last(explode('/', $wrappedType)); + return match ($lastSegment) { + ValidationException::ERROR_CODE => 'INVALID_ARGUMENT', + DeleteShortUrlException::ERROR_CODE => 'INVALID_SHORT_URL_DELETION', + DomainNotFoundException::ERROR_CODE => 'DOMAIN_NOT_FOUND', + ForbiddenTagOperationException::ERROR_CODE => 'FORBIDDEN_OPERATION', + InvalidUrlException::ERROR_CODE => 'INVALID_URL', + NonUniqueSlugException::ERROR_CODE => 'INVALID_SLUG', + ShortUrlNotFoundException::ERROR_CODE => 'INVALID_SHORTCODE', + TagConflictException::ERROR_CODE => 'TAG_CONFLICT', + TagNotFoundException::ERROR_CODE => 'TAG_NOT_FOUND', + MercureException::ERROR_CODE => 'MERCURE_NOT_CONFIGURED', + MissingAuthenticationException::ERROR_CODE => 'INVALID_AUTHORIZATION', + VerifyAuthenticationException::ERROR_CODE => 'INVALID_API_KEY', default => $wrappedType, }; } diff --git a/module/Rest/src/Exception/MercureException.php b/module/Rest/src/Exception/MercureException.php index 0e9a6edb..7e47b519 100644 --- a/module/Rest/src/Exception/MercureException.php +++ b/module/Rest/src/Exception/MercureException.php @@ -8,12 +8,14 @@ use Fig\Http\Message\StatusCodeInterface; use Mezzio\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait; use Mezzio\ProblemDetails\Exception\ProblemDetailsExceptionInterface; +use function Shlinkio\Shlink\Core\toProblemDetailsType; + class MercureException extends RuntimeException implements ProblemDetailsExceptionInterface { use CommonProblemDetailsExceptionTrait; private const TITLE = 'Mercure integration not configured'; - public const TYPE = 'https://shlink.io/api/error/mercure-not-configured'; + public const ERROR_CODE = 'mercure-not-configured'; public static function mercureNotConfigured(): self { @@ -21,7 +23,7 @@ class MercureException extends RuntimeException implements ProblemDetailsExcepti $e->detail = $e->getMessage(); $e->title = self::TITLE; - $e->type = self::TYPE; + $e->type = toProblemDetailsType(self::ERROR_CODE); $e->status = StatusCodeInterface::STATUS_NOT_IMPLEMENTED; return $e; diff --git a/module/Rest/src/Exception/MissingAuthenticationException.php b/module/Rest/src/Exception/MissingAuthenticationException.php index 3c1f5b9f..3fd2e2c6 100644 --- a/module/Rest/src/Exception/MissingAuthenticationException.php +++ b/module/Rest/src/Exception/MissingAuthenticationException.php @@ -9,6 +9,7 @@ use Mezzio\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait; use Mezzio\ProblemDetails\Exception\ProblemDetailsExceptionInterface; use function implode; +use function Shlinkio\Shlink\Core\toProblemDetailsType; use function sprintf; class MissingAuthenticationException extends RuntimeException implements ProblemDetailsExceptionInterface @@ -16,7 +17,7 @@ class MissingAuthenticationException extends RuntimeException implements Problem use CommonProblemDetailsExceptionTrait; private const TITLE = 'Invalid authorization'; - public const TYPE = 'https://shlink.io/api/error/missing-authentication'; + public const ERROR_CODE = 'missing-authentication'; public static function forHeaders(array $expectedHeaders): self { @@ -43,7 +44,7 @@ class MissingAuthenticationException extends RuntimeException implements Problem $e->detail = $message; $e->title = self::TITLE; - $e->type = self::TYPE; + $e->type = toProblemDetailsType(self::ERROR_CODE); $e->status = StatusCodeInterface::STATUS_UNAUTHORIZED; return $e; diff --git a/module/Rest/src/Exception/VerifyAuthenticationException.php b/module/Rest/src/Exception/VerifyAuthenticationException.php index 25541d03..25f1b050 100644 --- a/module/Rest/src/Exception/VerifyAuthenticationException.php +++ b/module/Rest/src/Exception/VerifyAuthenticationException.php @@ -8,11 +8,13 @@ use Fig\Http\Message\StatusCodeInterface; use Mezzio\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait; use Mezzio\ProblemDetails\Exception\ProblemDetailsExceptionInterface; +use function Shlinkio\Shlink\Core\toProblemDetailsType; + class VerifyAuthenticationException extends RuntimeException implements ProblemDetailsExceptionInterface { use CommonProblemDetailsExceptionTrait; - public const TYPE = 'https://shlink.io/api/error/invalid-api-key'; + public const ERROR_CODE = 'invalid-api-key'; public static function forInvalidApiKey(): self { @@ -20,7 +22,7 @@ class VerifyAuthenticationException extends RuntimeException implements ProblemD $e->detail = $e->getMessage(); $e->title = 'Invalid API key'; - $e->type = self::TYPE; + $e->type = toProblemDetailsType(self::ERROR_CODE); $e->status = StatusCodeInterface::STATUS_UNAUTHORIZED; return $e; diff --git a/module/Rest/test/Exception/BackwardsCompatibleProblemDetailsExceptionTest.php b/module/Rest/test/Exception/BackwardsCompatibleProblemDetailsExceptionTest.php index 13df168b..c63cee71 100644 --- a/module/Rest/test/Exception/BackwardsCompatibleProblemDetailsExceptionTest.php +++ b/module/Rest/test/Exception/BackwardsCompatibleProblemDetailsExceptionTest.php @@ -98,17 +98,17 @@ class BackwardsCompatibleProblemDetailsExceptionTest extends TestCase { yield ['foo', 'foo', true]; yield ['bar', 'bar', true]; - yield [ValidationException::TYPE, 'INVALID_ARGUMENT']; - yield [DeleteShortUrlException::TYPE, 'INVALID_SHORT_URL_DELETION']; - yield [DomainNotFoundException::TYPE, 'DOMAIN_NOT_FOUND']; - yield [ForbiddenTagOperationException::TYPE, 'FORBIDDEN_OPERATION']; - yield [InvalidUrlException::TYPE, 'INVALID_URL']; - yield [NonUniqueSlugException::TYPE, 'INVALID_SLUG']; - yield [ShortUrlNotFoundException::TYPE, 'INVALID_SHORTCODE']; - yield [TagConflictException::TYPE, 'TAG_CONFLICT']; - yield [TagNotFoundException::TYPE, 'TAG_NOT_FOUND']; - yield [MercureException::TYPE, 'MERCURE_NOT_CONFIGURED']; - yield [MissingAuthenticationException::TYPE, 'INVALID_AUTHORIZATION']; - yield [VerifyAuthenticationException::TYPE, 'INVALID_API_KEY']; + yield [ValidationException::ERROR_CODE, 'INVALID_ARGUMENT']; + yield [DeleteShortUrlException::ERROR_CODE, 'INVALID_SHORT_URL_DELETION']; + yield [DomainNotFoundException::ERROR_CODE, 'DOMAIN_NOT_FOUND']; + yield [ForbiddenTagOperationException::ERROR_CODE, 'FORBIDDEN_OPERATION']; + yield [InvalidUrlException::ERROR_CODE, 'INVALID_URL']; + yield [NonUniqueSlugException::ERROR_CODE, 'INVALID_SLUG']; + yield [ShortUrlNotFoundException::ERROR_CODE, 'INVALID_SHORTCODE']; + yield [TagConflictException::ERROR_CODE, 'TAG_CONFLICT']; + yield [TagNotFoundException::ERROR_CODE, 'TAG_NOT_FOUND']; + yield [MercureException::ERROR_CODE, 'MERCURE_NOT_CONFIGURED']; + yield [MissingAuthenticationException::ERROR_CODE, 'INVALID_AUTHORIZATION']; + yield [VerifyAuthenticationException::ERROR_CODE, 'INVALID_API_KEY']; } }