mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
Added as much additional data as possible to exceptions
This commit is contained in:
parent
fffb2872ef
commit
5266743a0c
@ -83,7 +83,7 @@ class DeleteShortUrlCommandTest extends TestCase
|
|||||||
$ignoreThreshold = array_pop($args);
|
$ignoreThreshold = array_pop($args);
|
||||||
|
|
||||||
if (!$ignoreThreshold) {
|
if (!$ignoreThreshold) {
|
||||||
throw new Exception\DeleteShortUrlException(10);
|
throw Exception\DeleteShortUrlException::fromVisitsThreshold(10, '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -112,7 +112,7 @@ class DeleteShortUrlCommandTest extends TestCase
|
|||||||
{
|
{
|
||||||
$shortCode = 'abc123';
|
$shortCode = 'abc123';
|
||||||
$deleteByShortCode = $this->service->deleteByShortCode($shortCode, false)->willThrow(
|
$deleteByShortCode = $this->service->deleteByShortCode($shortCode, false)->willThrow(
|
||||||
new Exception\DeleteShortUrlException(10)
|
Exception\DeleteShortUrlException::fromVisitsThreshold(10, '')
|
||||||
);
|
);
|
||||||
$this->commandTester->setInputs(['no']);
|
$this->commandTester->setInputs(['no']);
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||||||
namespace Shlinkio\Shlink\Core\Exception;
|
namespace Shlinkio\Shlink\Core\Exception;
|
||||||
|
|
||||||
use Fig\Http\Message\StatusCodeInterface;
|
use Fig\Http\Message\StatusCodeInterface;
|
||||||
use Throwable;
|
|
||||||
use Zend\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait;
|
use Zend\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait;
|
||||||
use Zend\ProblemDetails\Exception\ProblemDetailsExceptionInterface;
|
use Zend\ProblemDetails\Exception\ProblemDetailsExceptionInterface;
|
||||||
|
|
||||||
@ -18,18 +17,9 @@ class DeleteShortUrlException extends DomainException implements ProblemDetailsE
|
|||||||
private const TITLE = 'Cannot delete short URL';
|
private const TITLE = 'Cannot delete short URL';
|
||||||
private const TYPE = 'INVALID_SHORTCODE_DELETION'; // FIXME Should be INVALID_SHORT_URL_DELETION
|
private const TYPE = 'INVALID_SHORTCODE_DELETION'; // FIXME Should be INVALID_SHORT_URL_DELETION
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
private $visitsThreshold;
|
|
||||||
|
|
||||||
public function __construct(int $visitsThreshold, string $message = '', int $code = 0, ?Throwable $previous = null)
|
|
||||||
{
|
|
||||||
$this->visitsThreshold = $visitsThreshold;
|
|
||||||
parent::__construct($message, $code, $previous);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fromVisitsThreshold(int $threshold, string $shortCode): self
|
public static function fromVisitsThreshold(int $threshold, string $shortCode): self
|
||||||
{
|
{
|
||||||
$e = new self($threshold, sprintf(
|
$e = new self(sprintf(
|
||||||
'Impossible to delete short URL with short code "%s" since it has more than "%s" visits.',
|
'Impossible to delete short URL with short code "%s" since it has more than "%s" visits.',
|
||||||
$shortCode,
|
$shortCode,
|
||||||
$threshold
|
$threshold
|
||||||
@ -39,13 +29,16 @@ class DeleteShortUrlException extends DomainException implements ProblemDetailsE
|
|||||||
$e->title = self::TITLE;
|
$e->title = self::TITLE;
|
||||||
$e->type = self::TYPE;
|
$e->type = self::TYPE;
|
||||||
$e->status = StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY;
|
$e->status = StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY;
|
||||||
$e->additional = ['threshold' => $threshold];
|
$e->additional = [
|
||||||
|
'shortCode' => $shortCode,
|
||||||
|
'threshold' => $threshold,
|
||||||
|
];
|
||||||
|
|
||||||
return $e;
|
return $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getVisitsThreshold(): int
|
public function getVisitsThreshold(): int
|
||||||
{
|
{
|
||||||
return $this->visitsThreshold;
|
return $this->additional['threshold'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ class InvalidUrlException extends DomainException implements ProblemDetailsExcep
|
|||||||
$e->title = self::TITLE;
|
$e->title = self::TITLE;
|
||||||
$e->type = self::TYPE;
|
$e->type = self::TYPE;
|
||||||
$e->status = $status;
|
$e->status = $status;
|
||||||
|
$e->additional = ['url' => $url];
|
||||||
|
|
||||||
return $e;
|
return $e;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,11 @@ class NonUniqueSlugException extends InvalidArgumentException implements Problem
|
|||||||
$e->title = self::TITLE;
|
$e->title = self::TITLE;
|
||||||
$e->type = self::TYPE;
|
$e->type = self::TYPE;
|
||||||
$e->status = StatusCodeInterface::STATUS_BAD_REQUEST;
|
$e->status = StatusCodeInterface::STATUS_BAD_REQUEST;
|
||||||
|
$e->additional = ['customSlug' => $slug];
|
||||||
|
|
||||||
|
if ($domain !== null) {
|
||||||
|
$e->additional['domain'] = $domain;
|
||||||
|
}
|
||||||
|
|
||||||
return $e;
|
return $e;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,11 @@ class ShortUrlNotFoundException extends DomainException implements ProblemDetail
|
|||||||
$e->title = self::TITLE;
|
$e->title = self::TITLE;
|
||||||
$e->type = self::TYPE;
|
$e->type = self::TYPE;
|
||||||
$e->status = StatusCodeInterface::STATUS_NOT_FOUND;
|
$e->status = StatusCodeInterface::STATUS_NOT_FOUND;
|
||||||
|
$e->additional = ['shortCode' => $shortCode];
|
||||||
|
|
||||||
|
if ($domain !== null) {
|
||||||
|
$e->additional['domain'] = $domain;
|
||||||
|
}
|
||||||
|
|
||||||
return $e;
|
return $e;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ class TagNotFoundException extends DomainException implements ProblemDetailsExce
|
|||||||
$e->title = self::TITLE;
|
$e->title = self::TITLE;
|
||||||
$e->type = self::TYPE;
|
$e->type = self::TYPE;
|
||||||
$e->status = StatusCodeInterface::STATUS_NOT_FOUND;
|
$e->status = StatusCodeInterface::STATUS_NOT_FOUND;
|
||||||
|
$e->additional = ['tag' => $tag];
|
||||||
|
|
||||||
return $e;
|
return $e;
|
||||||
}
|
}
|
||||||
|
@ -24,19 +24,6 @@ class ValidationException extends InvalidArgumentException implements ProblemDet
|
|||||||
private const TITLE = 'Invalid data';
|
private const TITLE = 'Invalid data';
|
||||||
private const TYPE = 'INVALID_ARGUMENT';
|
private const TYPE = 'INVALID_ARGUMENT';
|
||||||
|
|
||||||
/** @var array */
|
|
||||||
private $invalidElements;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
string $message = '',
|
|
||||||
array $invalidElements = [],
|
|
||||||
int $code = 0,
|
|
||||||
?Throwable $previous = null
|
|
||||||
) {
|
|
||||||
$this->invalidElements = $invalidElements;
|
|
||||||
parent::__construct($message, $code, $previous);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fromInputFilter(InputFilterInterface $inputFilter, ?Throwable $prev = null): self
|
public static function fromInputFilter(InputFilterInterface $inputFilter, ?Throwable $prev = null): self
|
||||||
{
|
{
|
||||||
return static::fromArray($inputFilter->getMessages(), $prev);
|
return static::fromArray($inputFilter->getMessages(), $prev);
|
||||||
@ -45,22 +32,20 @@ class ValidationException extends InvalidArgumentException implements ProblemDet
|
|||||||
public static function fromArray(array $invalidData, ?Throwable $prev = null): self
|
public static function fromArray(array $invalidData, ?Throwable $prev = null): self
|
||||||
{
|
{
|
||||||
$status = StatusCodeInterface::STATUS_BAD_REQUEST;
|
$status = StatusCodeInterface::STATUS_BAD_REQUEST;
|
||||||
$e = new self('Provided data is not valid', $invalidData, $status, $prev);
|
$e = new self('Provided data is not valid', $status, $prev);
|
||||||
|
|
||||||
$e->detail = $e->getMessage();
|
$e->detail = $e->getMessage();
|
||||||
$e->title = self::TITLE;
|
$e->title = self::TITLE;
|
||||||
$e->type = self::TYPE;
|
$e->type = self::TYPE;
|
||||||
$e->status = StatusCodeInterface::STATUS_BAD_REQUEST;
|
$e->status = StatusCodeInterface::STATUS_BAD_REQUEST;
|
||||||
$e->additional = [
|
$e->additional = ['invalidElements' => $invalidData];
|
||||||
'invalidElements' => $invalidData,
|
|
||||||
];
|
|
||||||
|
|
||||||
return $e;
|
return $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getInvalidElements(): array
|
public function getInvalidElements(): array
|
||||||
{
|
{
|
||||||
return $this->invalidElements;
|
return $this->additional['invalidElements'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __toString(): string
|
public function __toString(): string
|
||||||
@ -80,7 +65,7 @@ class ValidationException extends InvalidArgumentException implements ProblemDet
|
|||||||
|
|
||||||
private function invalidElementsToString(): string
|
private function invalidElementsToString(): string
|
||||||
{
|
{
|
||||||
return reduce_left($this->invalidElements, function ($messageSet, string $name, $_, string $acc) {
|
return reduce_left($this->getInvalidElements(), function ($messageSet, string $name, $_, string $acc) {
|
||||||
return $acc . sprintf(
|
return $acc . sprintf(
|
||||||
"\n '%s' => %s",
|
"\n '%s' => %s",
|
||||||
$name,
|
$name,
|
||||||
|
@ -5,17 +5,15 @@ declare(strict_types=1);
|
|||||||
namespace ShlinkioTest\Shlink\Core\Exception;
|
namespace ShlinkioTest\Shlink\Core\Exception;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Shlinkio\Shlink\Common\Util\StringUtilsTrait;
|
|
||||||
use Shlinkio\Shlink\Core\Exception\DeleteShortUrlException;
|
use Shlinkio\Shlink\Core\Exception\DeleteShortUrlException;
|
||||||
|
|
||||||
use function Functional\map;
|
use function Functional\map;
|
||||||
use function range;
|
use function range;
|
||||||
|
use function Shlinkio\Shlink\Core\generateRandomShortCode;
|
||||||
use function sprintf;
|
use function sprintf;
|
||||||
|
|
||||||
class DeleteShortUrlExceptionTest extends TestCase
|
class DeleteShortUrlExceptionTest extends TestCase
|
||||||
{
|
{
|
||||||
use StringUtilsTrait;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @dataProvider provideThresholds
|
* @dataProvider provideThresholds
|
||||||
@ -29,29 +27,12 @@ class DeleteShortUrlExceptionTest extends TestCase
|
|||||||
|
|
||||||
$this->assertEquals($threshold, $e->getVisitsThreshold());
|
$this->assertEquals($threshold, $e->getVisitsThreshold());
|
||||||
$this->assertEquals($expectedMessage, $e->getMessage());
|
$this->assertEquals($expectedMessage, $e->getMessage());
|
||||||
$this->assertEquals(0, $e->getCode());
|
|
||||||
$this->assertNull($e->getPrevious());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @test
|
|
||||||
* @dataProvider provideThresholds
|
|
||||||
*/
|
|
||||||
public function visitsThresholdIsProperlyReturned(int $threshold): void
|
|
||||||
{
|
|
||||||
$e = new DeleteShortUrlException($threshold);
|
|
||||||
|
|
||||||
$this->assertEquals($threshold, $e->getVisitsThreshold());
|
|
||||||
$this->assertEquals('', $e->getMessage());
|
|
||||||
$this->assertEquals(0, $e->getCode());
|
|
||||||
$this->assertNull($e->getPrevious());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideThresholds(): array
|
public function provideThresholds(): array
|
||||||
{
|
{
|
||||||
return map(range(5, 50, 5), function (int $number) {
|
return map(range(5, 50, 5), function (int $number) {
|
||||||
$shortCode = $this->generateRandomString(6);
|
return [$number, $shortCode = generateRandomShortCode(6), sprintf(
|
||||||
return [$number, $shortCode, sprintf(
|
|
||||||
'Impossible to delete short URL with short code "%s" since it has more than "%s" visits.',
|
'Impossible to delete short URL with short code "%s" since it has more than "%s" visits.',
|
||||||
$shortCode,
|
$shortCode,
|
||||||
$number
|
$number
|
||||||
|
@ -13,38 +13,9 @@ use Throwable;
|
|||||||
use Zend\InputFilter\InputFilterInterface;
|
use Zend\InputFilter\InputFilterInterface;
|
||||||
|
|
||||||
use function print_r;
|
use function print_r;
|
||||||
use function random_int;
|
|
||||||
|
|
||||||
class ValidationExceptionTest extends TestCase
|
class ValidationExceptionTest extends TestCase
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @test
|
|
||||||
* @dataProvider provideExceptionData
|
|
||||||
*/
|
|
||||||
public function createsExceptionWrappingExpectedData(
|
|
||||||
array $args,
|
|
||||||
string $expectedMessage,
|
|
||||||
array $expectedInvalidElements,
|
|
||||||
int $expectedCode,
|
|
||||||
?Throwable $expectedPrev
|
|
||||||
): void {
|
|
||||||
$e = new ValidationException(...$args);
|
|
||||||
|
|
||||||
$this->assertEquals($expectedMessage, $e->getMessage());
|
|
||||||
$this->assertEquals($expectedInvalidElements, $e->getInvalidElements());
|
|
||||||
$this->assertEquals($expectedCode, $e->getCode());
|
|
||||||
$this->assertEquals($expectedPrev, $e->getPrevious());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function provideExceptionData(): iterable
|
|
||||||
{
|
|
||||||
yield 'empty args' => [[], '', [], 0, null];
|
|
||||||
yield 'with message' => [['something'], 'something', [], 0, null];
|
|
||||||
yield 'with elements' => [['something_else', [1, 2, 3]], 'something_else', [1, 2, 3], 0, null];
|
|
||||||
yield 'with code' => [['foo', [], $foo = random_int(-100, 100)], 'foo', [], $foo, null];
|
|
||||||
yield 'with prev' => [['bar', [], 8, $e = new RuntimeException()], 'bar', [], 8, $e];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @dataProvider provideExceptions
|
* @dataProvider provideExceptions
|
||||||
|
Loading…
Reference in New Issue
Block a user