Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"behat/behat": "~3.0",
"squizlabs/php_codesniffer": "3.*",
"phpunit/phpunit": "^7.0 || ^8.0",
"phpspec/prophecy": "^1.0",
"yoanm/php-unit-extended": "^1.0",
"yoanm/jsonrpc-server-doc-sdk": "^0.2"
}
Expand Down
30 changes: 28 additions & 2 deletions src/App/Serialization/JsonRpcResponseNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ class JsonRpcResponseNormalizer
const SUB_KEY_ERROR_MESSAGE = 'message';
const SUB_KEY_ERROR_DATA = 'data';

/**
* @var bool whether to display debug data for the errors.
*/
protected $debug = false;

public function __construct(bool $debug = false)
{
$this->debug = $debug;
}

/**
* @param JsonRpcResponse $response
*
Expand Down Expand Up @@ -58,10 +68,26 @@ private function normalizeError(JsonRpcExceptionInterface $error) : array
self::SUB_KEY_ERROR_MESSAGE => $error->getErrorMessage()
];

if ($error->getErrorData()) {
$normalizedError[self::SUB_KEY_ERROR_DATA] = $error->getErrorData();
$errorData = $error->getErrorData();

if ($this->debug) {
$errorData += $this->composeDebugErrorData($error->getPrevious() ?? $error);
}


if (!empty($errorData)) {
$normalizedError[self::SUB_KEY_ERROR_DATA] = $errorData;
}

return $normalizedError;
}

private function composeDebugErrorData(\Throwable $error) : array
{
return [
'_code' => $error->getCode(),
'_message' => $error->getMessage(),
'_trace' => $error->getTrace(),
];
}
}
19 changes: 15 additions & 4 deletions src/Domain/Exception/JsonRpcException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
namespace Yoanm\JsonRpcServer\Domain\Exception;

/**
* Class JsonRpcException
* JsonRpcException represents an error which can be rendered as a JsonRpc response.
* It allows the specification of the particular data to be displayed making error more verbose.
*
* > Note: be careful about the data you expose via this mechanism, make sure you do not expose any vital information through it.
*
* @see \Yoanm\JsonRpcServer\App\Serialization\JsonRpcResponseNormalizer
*/
class JsonRpcException extends \Exception implements JsonRpcExceptionInterface
{
Expand All @@ -14,25 +19,31 @@ class JsonRpcException extends \Exception implements JsonRpcExceptionInterface
* @param string $message
* @param array $data
*/
public function __construct(int $code, string $message = '', array $data = [])
public function __construct(int $code, string $message = '', array $data = [], ?\Throwable $previous = null)
{
$this->data = $data;

parent::__construct($message, $code);
parent::__construct($message, $code, $previous);
}

/**
* {@inheritdoc}
*/
public function getErrorCode() : int
{
return parent::getCode();
}

/**
* {@inheritdoc}
*/
public function getErrorMessage() : string
{
return parent::getMessage();
}

/**
* @return array
* {@inheritdoc}
*/
public function getErrorData() : array
{
Expand Down
2 changes: 1 addition & 1 deletion src/Domain/Exception/JsonRpcExceptionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/**
* Interface JsonRpcExceptionInterface
*/
interface JsonRpcExceptionInterface
interface JsonRpcExceptionInterface extends \Throwable
{
/**
* @return int JsonRpc error code
Expand Down
12 changes: 8 additions & 4 deletions src/Domain/Exception/JsonRpcInternalErrorException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@
namespace Yoanm\JsonRpcServer\Domain\Exception;

/**
* Class JsonRpcInternalErrorException
* JsonRpcInternalErrorException represents unhandled error during JsonRpc method processing (e.g. "Internal server error").
*/
class JsonRpcInternalErrorException extends JsonRpcException
{
const CODE = -32603;

/**
* @deprecated no longer in use, will be removed at next major release.
*/
const DATA_PREVIOUS_KEY = 'previous';

/**
* @param \Exception|null $previousException
* @param \Throwable|null $previousException
*/
public function __construct(\Exception $previousException = null)
public function __construct(\Throwable $previousException = null)
{
parent::__construct(
self::CODE,
'Internal error',
$previousException ? [self::DATA_PREVIOUS_KEY => $previousException->getMessage()] : []
[],
$previousException
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use PHPUnit\Framework\TestCase;
use Yoanm\JsonRpcServer\App\Serialization\JsonRpcResponseNormalizer;
use Yoanm\JsonRpcServer\Domain\Exception\JsonRpcException;
use Yoanm\JsonRpcServer\Domain\Exception\JsonRpcInternalErrorException;
use Yoanm\JsonRpcServer\Domain\Model\JsonRpcResponse;

/**
Expand Down Expand Up @@ -132,4 +133,55 @@ public function testShouldNormalizeErrorWithData()
$this->assertArrayHasKey(self::EXPECTED_SUB_KEY_ERROR_DATA, $errorObject, 'Error data not found');
$this->assertSame($data, $errorObject[self::EXPECTED_SUB_KEY_ERROR_DATA], 'Error data not expected');
}

public function testShouldConcealErrorDataWithoutDebug()
{
$this->responseNormalizer = new JsonRpcResponseNormalizer(false);

$exceptionMessage = 'Test exception';
$exceptionCode = 12345;

try {
throw new \RuntimeException($exceptionMessage, $exceptionCode);
} catch (\Throwable $exception) {
// shutdown test exception as prepared
}

$response = (new JsonRpcResponse())
->setError(new JsonRpcInternalErrorException($exception));

$result = $this->responseNormalizer->normalize($response);

$this->assertTrue(empty($result[self::EXPECTED_KEY_ERROR][self::EXPECTED_SUB_KEY_ERROR_DATA]));
}

public function testShouldShowErrorDataWithDebug()
{
$this->responseNormalizer = new JsonRpcResponseNormalizer(true);

$exceptionMessage = 'Test exception';
$exceptionCode = 12345;

try {
throw new \RuntimeException($exceptionMessage, $exceptionCode);
} catch (\Throwable $exception) {
// shutdown test exception as prepared
}

$response = (new JsonRpcResponse())
->setError(new JsonRpcInternalErrorException($exception));

$result = $this->responseNormalizer->normalize($response);

$this->assertFalse(empty($result[self::EXPECTED_KEY_ERROR][self::EXPECTED_SUB_KEY_ERROR_DATA]));

$debugData = $result[self::EXPECTED_KEY_ERROR][self::EXPECTED_SUB_KEY_ERROR_DATA];

$this->assertFalse(empty($debugData['_code']));
$this->assertFalse(empty($debugData['_message']));
$this->assertFalse(empty($debugData['_trace']));

$this->assertSame($exceptionMessage, $debugData['_message']);
$this->assertSame($exceptionCode, $debugData['_code']);
}
}
2 changes: 1 addition & 1 deletion tests/Functional/Domain/Exception/JsonRpcExceptionTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
namespace Tests\Technical\Domain\Exception;
namespace Tests\Functional\Domain\Exception;

use PHPUnit\Framework\TestCase;
use Yoanm\JsonRpcServer\Domain\Exception\JsonRpcException;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
namespace Tests\Technical\Domain\Exception;
namespace Tests\Functional\Domain\Exception;

use PHPUnit\Framework\TestCase;
use Yoanm\JsonRpcServer\Domain\Exception\JsonRpcInternalErrorException;
Expand All @@ -25,13 +25,7 @@ public function testShouldHandleAnExceptionAnPutItInExceptionData()

$exception = new JsonRpcInternalErrorException($previousException);

$this->assertArrayHasKey(
JsonRpcInternalErrorException::DATA_PREVIOUS_KEY,
$exception->getErrorData()
);
$this->assertSame(
$message,
$exception->getErrorData()[JsonRpcInternalErrorException::DATA_PREVIOUS_KEY]
);
$this->assertEmpty($exception->getErrorData());
$this->assertSame($previousException, $exception->getPrevious());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
namespace Tests\Technical\Domain\Exception;
namespace Tests\Functional\Domain\Exception;

use PHPUnit\Framework\TestCase;
use Yoanm\JsonRpcServer\Domain\Exception\JsonRpcInvalidParamsException;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
namespace Tests\Technical\Domain\Exception;
namespace Tests\Functional\Domain\Exception;

use PHPUnit\Framework\TestCase;
use Yoanm\JsonRpcServer\Domain\Exception\JsonRpcInvalidRequestException;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
namespace Tests\Technical\Domain\Exception;
namespace Tests\Functional\Domain\Exception;

use PHPUnit\Framework\TestCase;
use Yoanm\JsonRpcServer\Domain\Exception\JsonRpcMethodNotFoundException;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
namespace Tests\Technical\Domain\Exception;
namespace Tests\Functional\Domain\Exception;

use PHPUnit\Framework\TestCase;
use Yoanm\JsonRpcServer\Domain\Exception\JsonRpcParseErrorException;
Expand Down
6 changes: 2 additions & 4 deletions tests/Technical/App/Creator/ResponseCreatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ public function testCreateErrorResponseConvertOtherExceptions()
$response = $this->responseCreator->createErrorResponse($exception);

$this->assertInstanceOf(JsonRpcInternalErrorException::class, $response->getError());
$this->assertSame(
$message,
$response->getError()->getErrorData()[JsonRpcInternalErrorException::DATA_PREVIOUS_KEY]
);
$this->assertEmpty($response->getError()->getErrorData());
$this->assertSame($exception, $response->getError()->getPrevious());
}
}