Skip to content

#81: add JsonRpcResponseNormalizer::$debug #82

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Apr 8, 2023
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());
}
}