Converted NonUniqueSlugException into a problem details exception

This commit is contained in:
Alejandro Celaya 2019-11-24 23:32:37 +01:00
parent 0d7d53ab5b
commit c1eee2246b
5 changed files with 26 additions and 41 deletions

View file

@ -4,10 +4,19 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Exception;
use Fig\Http\Message\StatusCodeInterface;
use Zend\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait;
use Zend\ProblemDetails\Exception\ProblemDetailsExceptionInterface;
use function sprintf;
class NonUniqueSlugException extends InvalidArgumentException
class NonUniqueSlugException extends InvalidArgumentException implements ProblemDetailsExceptionInterface
{
use CommonProblemDetailsExceptionTrait;
private const TITLE = 'Invalid custom slug';
public const TYPE = 'INVALID_SLUG';
public static function fromSlug(string $slug, ?string $domain): self
{
$suffix = '';
@ -15,6 +24,13 @@ class NonUniqueSlugException extends InvalidArgumentException
$suffix = sprintf(' for domain "%s"', $domain);
}
return new self(sprintf('Provided slug "%s" is not unique%s.', $slug, $suffix));
$e = new self(sprintf('Provided slug "%s" is already in use%s.', $slug, $suffix));
$e->detail = $e->getMessage();
$e->title = self::TITLE;
$e->type = self::TYPE;
$e->status = StatusCodeInterface::STATUS_BAD_REQUEST;
return $e;
}
}

View file

@ -22,12 +22,12 @@ class NonUniqueSlugExceptionTest extends TestCase
public function provideMessages(): iterable
{
yield 'without domain' => [
'Provided slug "foo" is not unique.',
'Provided slug "foo" is already in use.',
'foo',
null,
];
yield 'with domain' => [
'Provided slug "baz" is not unique for domain "bar".',
'Provided slug "baz" is already in use for domain "bar".',
'baz',
'bar',
];

View file

@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Rest\Action\ShortUrl;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
use Shlinkio\Shlink\Core\Exception\ValidationException;
use Shlinkio\Shlink\Core\Model\CreateShortUrlData;
use Shlinkio\Shlink\Core\Service\UrlShortenerInterface;
@ -16,8 +15,6 @@ use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
use Shlinkio\Shlink\Rest\Util\RestUtils;
use Zend\Diactoros\Response\JsonResponse;
use function sprintf;
abstract class AbstractCreateShortUrlAction extends AbstractRestAction
{
/** @var UrlShortenerInterface */
@ -52,21 +49,13 @@ abstract class AbstractCreateShortUrlAction extends AbstractRestAction
}
$longUrl = $shortUrlData->getLongUrl();
$tags = $shortUrlData->getTags();
$shortUrlMeta = $shortUrlData->getMeta();
try {
$shortUrl = $this->urlShortener->urlToShortCode($longUrl, $shortUrlData->getTags(), $shortUrlMeta);
$transformer = new ShortUrlDataTransformer($this->domainConfig);
$shortUrl = $this->urlShortener->urlToShortCode($longUrl, $tags, $shortUrlMeta);
$transformer = new ShortUrlDataTransformer($this->domainConfig);
return new JsonResponse($transformer->transform($shortUrl));
} catch (NonUniqueSlugException $e) {
$customSlug = $shortUrlMeta->getCustomSlug();
$this->logger->warning('Provided non-unique slug. {e}', ['e' => $e]);
return new JsonResponse([
'error' => RestUtils::getRestErrorCodeFromException($e),
'message' => sprintf('Provided slug %s is already in use. Try with a different one.', $customSlug),
], self::STATUS_BAD_REQUEST);
}
return new JsonResponse($transformer->transform($shortUrl));
}
/**

View file

@ -19,7 +19,8 @@ class RestUtils
/** @deprecated */
public const INVALID_URL_ERROR = Core\InvalidUrlException::TYPE;
public const INVALID_ARGUMENT_ERROR = 'INVALID_ARGUMENT';
public const INVALID_SLUG_ERROR = 'INVALID_SLUG';
/** @deprecated */
public const INVALID_SLUG_ERROR = Core\NonUniqueSlugException::TYPE;
public const INVALID_CREDENTIALS_ERROR = 'INVALID_CREDENTIALS';
public const INVALID_AUTH_TOKEN_ERROR = 'INVALID_AUTH_TOKEN';
public const INVALID_AUTHORIZATION_ERROR = 'INVALID_AUTHORIZATION';

View file

@ -8,8 +8,6 @@ use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Service\UrlShortener;
use Shlinkio\Shlink\Rest\Action\ShortUrl\CreateShortUrlAction;
use Shlinkio\Shlink\Rest\Util\RestUtils;
@ -88,23 +86,4 @@ class CreateShortUrlActionTest extends TestCase
yield ['127.0.0.1'];
yield ['???/&%$&'];
}
/** @test */
public function nonUniqueSlugReturnsError(): void
{
$this->urlShortener->urlToShortCode(
Argument::type(Uri::class),
Argument::type('array'),
ShortUrlMeta::createFromRawData(['customSlug' => 'foo']),
Argument::cetera()
)->willThrow(NonUniqueSlugException::class)->shouldBeCalledOnce();
$request = (new ServerRequest())->withParsedBody([
'longUrl' => 'http://www.domain.com/foo/bar',
'customSlug' => 'foo',
]);
$response = $this->action->handle($request);
$this->assertEquals(400, $response->getStatusCode());
$this->assertStringContainsString(RestUtils::INVALID_SLUG_ERROR, (string) $response->getBody());
}
}