From 3d6b269f309bc5578d36746fc77fdd74323b8789 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Tue, 12 Apr 2016 21:04:03 +0300 Subject: [PATCH 1/2] Pre-release fixes * Review API, add some documentation * Add tests on JSON-RPC request formatting --- README.md | 15 +++- composer.json | 3 +- src/ScayTrase/Api/JsonRpc/JsonRpcClient.php | 81 ++++++++++--------- src/ScayTrase/Api/JsonRpc/JsonRpcError.php | 27 +++++-- .../Api/JsonRpc/JsonRpcErrorInterface.php | 36 ++++++++- .../Api/JsonRpc/JsonRpcNotification.php | 22 ++++- .../JsonRpc/JsonRpcNotificationResponse.php | 9 +++ src/ScayTrase/Api/JsonRpc/JsonRpcRequest.php | 34 +++++++- .../Api/JsonRpc/JsonRpcRequestFactory.php | 46 ----------- .../Api/JsonRpc/JsonRpcRequestInterface.php | 62 +++++++++++++- .../Api/JsonRpc/JsonRpcResponseCollection.php | 2 +- .../Api/JsonRpc/JsonRpcResponseInterface.php | 39 +++++++-- .../Api/JsonRpc/RequestTransformation.php | 2 +- .../Api/JsonRpc/ResponseBodyValidator.php | 8 +- src/ScayTrase/Api/JsonRpc/SyncResponse.php | 39 ++++++--- .../Api/JsonRpc/Tests/JsonRpcClientTest.php | 80 +++++++++++++++++- .../Tests/ResponseBodyValidatorTest.php | 8 +- src/ScayTrase/Api/Rpc/RpcRequestInterface.php | 2 +- .../Api/Rpc/RpcResponseInterface.php | 2 +- .../Api/Rpc/Tests/LazyClientTest.php | 2 +- 20 files changed, 387 insertions(+), 132 deletions(-) delete mode 100644 src/ScayTrase/Api/JsonRpc/JsonRpcRequestFactory.php diff --git a/README.md b/README.md index 6251e6e..773ce2b 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,17 @@ # RPC Library -## JSON-RPC Implementation +Built-in support for batch-like requests. Processing depends on client implementation (may not be real batch) + +## Common interfaces + * RPC request (call) + * RPC response (result) + * RPC error +## Decorators + * Lazy client decorator + * Loggable client decorator + * Cacheable client decorator + +# JSON-RPC Implementation + +[JSON-RPC 2.0 Specification](http://www.jsonrpc.org/specification) diff --git a/composer.json b/composer.json index bdd4ca3..34ca4e7 100644 --- a/composer.json +++ b/composer.json @@ -11,12 +11,12 @@ "minimum-stability": "stable", "require": { "php": "~5.5|~7.0", + "psr/log": "~1.0", "guzzlehttp/guzzle": "~6.0", "paragonie/random_compat": "~1.1@stable" }, "require-dev": { "phpunit/phpunit": "~4.5|~5.1", - "psr/log": "~1.0", "psr/cache": "~1.0" }, "autoload": { @@ -25,7 +25,6 @@ } }, "suggest": { - "psr/log": "For LoggableRpcClient decorator", "psr/cache": "For CacheableRpcClient decorator" } } diff --git a/src/ScayTrase/Api/JsonRpc/JsonRpcClient.php b/src/ScayTrase/Api/JsonRpc/JsonRpcClient.php index 5896e29..e619971 100644 --- a/src/ScayTrase/Api/JsonRpc/JsonRpcClient.php +++ b/src/ScayTrase/Api/JsonRpc/JsonRpcClient.php @@ -11,6 +11,8 @@ use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Psr7\Request; use Psr\Http\Message\UriInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use ScayTrase\Api\IdGenerator\IdGeneratorInterface; use ScayTrase\Api\IdGenerator\UuidGenerator; use ScayTrase\Api\Rpc\Exception\RemoteCallFailedException; @@ -29,22 +31,35 @@ final class JsonRpcClient implements RpcClientInterface private $uri; /** @var IdGeneratorInterface */ private $idGenerator; + /** @var LoggerInterface */ + private $logger; /** * JsonRpcClient constructor. * @param ClientInterface $client * @param UriInterface $endpoint * @param IdGeneratorInterface|null $idGenerator + * @param LoggerInterface $logger */ - public function __construct(ClientInterface $client, UriInterface $endpoint, IdGeneratorInterface $idGenerator = null) + public function __construct( + ClientInterface $client, + UriInterface $endpoint, + IdGeneratorInterface $idGenerator = null, + LoggerInterface $logger = null + ) { $this->client = $client; $this->uri = $endpoint; $this->idGenerator = $idGenerator; + $this->logger = $logger; if (null === $this->idGenerator) { $this->idGenerator = new UuidGenerator(); } + + if (null === $this->logger) { + $this->logger = new NullLogger(); + } } /** @@ -52,21 +67,40 @@ public function __construct(ClientInterface $client, UriInterface $endpoint, IdG */ public function invoke($calls) { - if (!is_array($calls) && ($calls instanceof RpcRequestInterface)) { - $calls = [$calls]; - } + try { + if (!is_array($calls) && ($calls instanceof RpcRequestInterface)) { + $transformedCall = $this->transformCall($calls); + return new JsonRpcResponseCollection( + $this->client->sendAsync( + $this->createHttpRequest($transformedCall) + ), + [$transformedCall] + ); + } + + $requests = []; + $batchRequest = []; - $requests = []; - $requestBody = []; + foreach ($calls as $key => $call) { + $transformedCall = $this->transformCall($call); + $requests[spl_object_hash($call)] = new RequestTransformation($call, $transformedCall); + $batchRequest[] = $transformedCall; + } - foreach ($calls as $key => $call) { - $transformedCall = $this->transformCall($call); - $requests[spl_object_hash($call)] = new RequestTransformation($call, $transformedCall); - $requestBody[] = $this->formatJsonRpcCall($transformedCall); + return new JsonRpcResponseCollection($this->client->sendAsync($this->createHttpRequest($batchRequest)), $requests); + } catch (GuzzleException $exception) { + throw new RemoteCallFailedException($exception->getMessage(), 0, $exception); } + } + /** + * @param $requestBody + * @return Request + */ + private function createHttpRequest($requestBody) + { /** @noinspection ExceptionsAnnotatingAndHandlingInspection */ - $request = new Request( + return new Request( 'POST', $this->uri, [ @@ -75,12 +109,6 @@ public function invoke($calls) ], json_encode($requestBody, JSON_PRETTY_PRINT) ); - - try { - return new JsonRpcResponseCollection($this->client->sendAsync($request), $requests); - } catch (GuzzleException $exception) { - throw new RemoteCallFailedException($exception->getMessage(), 0, $exception); - } } /** @@ -96,23 +124,4 @@ private function transformCall(RpcRequestInterface $call) } return $transformedCall; } - - /** - * @param JsonRpcRequestInterface|RpcRequestInterface $request - * @return array - */ - private function formatJsonRpcCall(JsonRpcRequestInterface $request) - { - $result = [ - 'jsonrpc' => static::VERSION, - 'method' => $request->getMethod(), - 'params' => $request->getParameters(), - ]; - - if (!$request->isNotification()) { - $result['id'] = $request->getId(); - } - - return $result; - } } diff --git a/src/ScayTrase/Api/JsonRpc/JsonRpcError.php b/src/ScayTrase/Api/JsonRpc/JsonRpcError.php index e0deaa8..4cfb33c 100644 --- a/src/ScayTrase/Api/JsonRpc/JsonRpcError.php +++ b/src/ScayTrase/Api/JsonRpc/JsonRpcError.php @@ -13,37 +13,52 @@ final class JsonRpcError implements JsonRpcErrorInterface private $code; /** @var string */ private $message; - /** @var null|\StdClass */ + /** @var null|\stdClass */ private $data; /** * JsonRpcError constructor. * @param int $code * @param string $message - * @param \StdClass|null $data + * @param \stdClass|mixed|null $data */ - public function __construct($code, $message, \StdClass $data = null) + public function __construct($code, $message, $data = null) { $this->code = $code; $this->message = $message; $this->data = $data; } - /** @return int */ + /** {@inheritdoc} */ public function getCode() { return $this->code; } - /** @return string */ + /** {@inheritdoc} */ public function getMessage() { return $this->message; } - /** @return \StdClass|null error data */ + /** {@inheritdoc} */ public function getData() { return $this->data; } + + /** {@inheritdoc} */ + public function jsonSerialize() + { + $error = [ + self::ERROR_CODE_FIELD => $this->getCode(), + self::ERROR_MESSAGE_FIELD => $this->getMessage(), + ]; + + if (null !== ($data = $this->getData())) { + $error[self::ERROR_DATA_FIELD] = $data; + } + + return $error; + } } diff --git a/src/ScayTrase/Api/JsonRpc/JsonRpcErrorInterface.php b/src/ScayTrase/Api/JsonRpc/JsonRpcErrorInterface.php index 7672022..6122d0c 100644 --- a/src/ScayTrase/Api/JsonRpc/JsonRpcErrorInterface.php +++ b/src/ScayTrase/Api/JsonRpc/JsonRpcErrorInterface.php @@ -9,14 +9,46 @@ use ScayTrase\Api\Rpc\RpcErrorInterface; -interface JsonRpcErrorInterface extends RpcErrorInterface +interface JsonRpcErrorInterface extends RpcErrorInterface, \JsonSerializable { + const ERROR_CODE_FIELD = 'code'; + const ERROR_MESSAGE_FIELD = 'message'; + const ERROR_DATA_FIELD = 'data'; + CONST PARSE_ERROR = -32700; CONST INVALID_REQUEST = -32600; CONST METHOD_NOT_FOUND = -32601; CONST INVALID_PARAMS = -32602; CONST INTERNAL_ERROR = -32603; - /** @return \StdClass|null error data */ + /** + * Returns error code + * + * A Number that indicates the error type that occurred. + * This MUST be an integer. + * + * @return int + */ + public function getCode(); + + /** + * Return error message + * + * String providing a short description of the error. + * The message SHOULD be limited to a concise single sentence. + * + * @return string + */ + public function getMessage(); + + /** + * Returns amy additional error information specified by server + * + * A Primitive or Structured value that contains additional information about the error. + * This may be omitted. + * The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.). + * + * @return \stdClass|mixed|null error data + */ public function getData(); } diff --git a/src/ScayTrase/Api/JsonRpc/JsonRpcNotification.php b/src/ScayTrase/Api/JsonRpc/JsonRpcNotification.php index c7b812b..970ad1a 100644 --- a/src/ScayTrase/Api/JsonRpc/JsonRpcNotification.php +++ b/src/ScayTrase/Api/JsonRpc/JsonRpcNotification.php @@ -11,15 +11,15 @@ final class JsonRpcNotification implements JsonRpcRequestInterface { /** @var string */ private $method; - /** @var array */ + /** @var \stdClass|array|null */ private $parameters; /** * JsonRpcNotificationRequest constructor. * @param string $method - * @param array $parameters + * @param \stdClass|array|null $parameters */ - public function __construct($method, array $parameters) + public function __construct($method, $parameters) { $this->method = $method; $this->parameters = $parameters; @@ -48,4 +48,20 @@ public function getParameters() { return $this->parameters; } + + /** {@inheritdoc} */ + public function getVersion() + { + return JsonRpcClient::VERSION; + } + + /** {@inheritdoc} */ + public function jsonSerialize() + { + return [ + self::VERSION_FIELD => JsonRpcClient::VERSION, + self::METHOD_FIELD => $this->getMethod(), + self::PARAMETERS_FIELD => $this->getParameters(), + ]; + } } diff --git a/src/ScayTrase/Api/JsonRpc/JsonRpcNotificationResponse.php b/src/ScayTrase/Api/JsonRpc/JsonRpcNotificationResponse.php index f8102db..5740a06 100644 --- a/src/ScayTrase/Api/JsonRpc/JsonRpcNotificationResponse.php +++ b/src/ScayTrase/Api/JsonRpc/JsonRpcNotificationResponse.php @@ -38,4 +38,13 @@ public function getId() { return null; } + + /** + * {@inheritdoc} + * @throws \LogicException + */ + public function jsonSerialize() + { + throw new \LogicException('Notification should not have transferable response representation'); + } } diff --git a/src/ScayTrase/Api/JsonRpc/JsonRpcRequest.php b/src/ScayTrase/Api/JsonRpc/JsonRpcRequest.php index d1c1de0..92f5294 100644 --- a/src/ScayTrase/Api/JsonRpc/JsonRpcRequest.php +++ b/src/ScayTrase/Api/JsonRpc/JsonRpcRequest.php @@ -15,13 +15,13 @@ final class JsonRpcRequest implements JsonRpcRequestInterface private $id; /** @var string */ private $method; - /** @var \StdClass|\StdClass[]|null */ + /** @var \stdClass|array|null */ private $parameters; /** * JsonRpcRequest constructor. * @param string $method - * @param \StdClass|\StdClass[]|null $parameters + * @param \stdClass|array|null $parameters * @param string $id */ public function __construct($method, $parameters = null, $id = null) @@ -64,4 +64,34 @@ public function getParameters() { return $this->parameters; } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() + { + $result = [ + self::VERSION_FIELD => JsonRpcClient::VERSION, + self::METHOD_FIELD => $this->getMethod(), + self::PARAMETERS_FIELD => $this->getParameters(), + ]; + + if (!$this->isNotification()) { + $result[self::ID_FIELD] = $this->getId(); + } + + return $result; + } + + /** + * Returns version of the JSON-RPC request + * + * A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0". + * + * @return string JSON-RPC version + */ + public function getVersion() + { + return JsonRpcClient::VERSION; + } } diff --git a/src/ScayTrase/Api/JsonRpc/JsonRpcRequestFactory.php b/src/ScayTrase/Api/JsonRpc/JsonRpcRequestFactory.php deleted file mode 100644 index 8996625..0000000 --- a/src/ScayTrase/Api/JsonRpc/JsonRpcRequestFactory.php +++ /dev/null @@ -1,46 +0,0 @@ -idGenerator = $idGenerator; - } - - /** - * @param $method - * @param array $parameters - * @param string|null $id - * @return JsonRpcRequestInterface - */ - public function createRequest($method, array $parameters, $id = null) - { - return new JsonRpcRequest($method, $parameters, $id ?: $this->idGenerator->getRequestIdentifier()); - } - - /** - * @param $method - * @param array $parameters - * @return JsonRpcRequestInterface - */ - public function createNotification($method, array $parameters) - { - return new JsonRpcNotification($method, $parameters); - } -} diff --git a/src/ScayTrase/Api/JsonRpc/JsonRpcRequestInterface.php b/src/ScayTrase/Api/JsonRpc/JsonRpcRequestInterface.php index ed1e406..b45b875 100644 --- a/src/ScayTrase/Api/JsonRpc/JsonRpcRequestInterface.php +++ b/src/ScayTrase/Api/JsonRpc/JsonRpcRequestInterface.php @@ -9,11 +9,67 @@ use ScayTrase\Api\Rpc\RpcRequestInterface; -interface JsonRpcRequestInterface extends RpcRequestInterface +/** + * Interface JsonRpcRequestInterface + * @link http://www.jsonrpc.org/specification + */ +interface JsonRpcRequestInterface extends RpcRequestInterface, \JsonSerializable { - /** @return int|null Id. if not a notification and id is not set - id should be automatically generated */ + const VERSION_FIELD = 'jsonrpc'; + const ID_FIELD = 'id'; + const PARAMETERS_FIELD = 'params'; + const METHOD_FIELD = 'method'; + + /** + * Returns version of the JSON-RPC request + * + * A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0". + * + * @return string JSON-RPC version + */ + public function getVersion(); + + /** + * Return JSON-RPC request identifier or NULL for notification request + * + * An identifier established by the Client that MUST contain a String, Number, or NULL value if included. + * If it is not included it is assumed to be a notification. + * + * @return int|null Id. if not a notification and id is not set - id should be automatically generated + */ public function getId(); - /** @return bool True if request should not receive response from the server */ + /** + * Indicates if request is notification + * + * @return bool True if request should not receive response from the server + */ public function isNotification(); + + /** + * Returns JSON-RPC request method + * + * A String containing the name of the method to be invoked. Method names that begin with the word rpc + * followed by a period character (U+002E or ASCII 46) are reserved for rpc-internal methods and extensions + * and MUST NOT be used for anything else. + * + * @return string + */ + public function getMethod(); + + /** + * Returns JSON-RPC request parameters or null if omitted + * + * A Structured value that holds the parameter values to be used during the invocation of the method. This member MAY be omitted. + * + * If present, parameters for the rpc call MUST be provided as a Structured value. Either by-position through an Array or by-name through an Object. + * + * * by-position: params MUST be an Array, containing the values in the Server expected order. + * * by-name: params MUST be an Object, with member names that match the Server expected parameter names. + * The absence of expected names MAY result in an error being generated. The names MUST match + * exactly, including case, to the method's expected parameters. + * + * @return \stdClass|array|null + */ + public function getParameters(); } diff --git a/src/ScayTrase/Api/JsonRpc/JsonRpcResponseCollection.php b/src/ScayTrase/Api/JsonRpc/JsonRpcResponseCollection.php index 4c4777c..1fe303c 100644 --- a/src/ScayTrase/Api/JsonRpc/JsonRpcResponseCollection.php +++ b/src/ScayTrase/Api/JsonRpc/JsonRpcResponseCollection.php @@ -78,7 +78,7 @@ private function sync() } - if (!is_array($rawResponses) && $rawResponses instanceof \StdClass) { + if (!is_array($rawResponses) && $rawResponses instanceof \stdClass) { $rawResponses = [$rawResponses]; } diff --git a/src/ScayTrase/Api/JsonRpc/JsonRpcResponseInterface.php b/src/ScayTrase/Api/JsonRpc/JsonRpcResponseInterface.php index 1155ee7..ea7260a 100644 --- a/src/ScayTrase/Api/JsonRpc/JsonRpcResponseInterface.php +++ b/src/ScayTrase/Api/JsonRpc/JsonRpcResponseInterface.php @@ -9,24 +9,51 @@ use ScayTrase\Api\Rpc\RpcResponseInterface; -interface JsonRpcResponseInterface extends RpcResponseInterface +interface JsonRpcResponseInterface extends RpcResponseInterface, \JsonSerializable { const VERSION_FIELD = 'jsonrpc'; const ID_FIELD = 'id'; const ERROR_FIELD = 'error'; - const ERROR_CODE_FIELD = 'code'; - const ERROR_MESSAGE_FIELD = 'message'; - const ERROR_DATA_FIELD = 'data'; const RESULT_FIELD = 'result'; /** + * Returns version of the JSON-RPC request + * + * A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0". + * * @return string JSON-RPC version */ public function getVersion(); - /** @return string|null Response ID or null for notification pseudo-response */ + /** + * Returns ID of response or NULL if request was notification + * + * This member is REQUIRED. + * It MUST be the same as the value of the id member in the Request Object. + * + * @return string|null Response ID or null for notification pseudo-response + */ public function getId(); - /** @return JsonRpcErrorInterface */ + /** + * Returns JSON-RPC Error object or null if request was successful + * + * This member is REQUIRED on error. + * This member MUST NOT exist if there was no error triggered during invocation. + * The value for this member MUST be an Object as defined in section 5.1. + * + * @return JsonRpcErrorInterface|null + */ public function getError(); + + /** + * Returns result of JSON-RPC request + * + * This member is REQUIRED on success. + * This member MUST NOT exist if there was an error invoking the method. + * The value of this member is determined by the method invoked on the Server. + * + * @return \stdClass|array|mixed|null + */ + public function getBody(); } diff --git a/src/ScayTrase/Api/JsonRpc/RequestTransformation.php b/src/ScayTrase/Api/JsonRpc/RequestTransformation.php index 5d9b5d5..b79b868 100644 --- a/src/ScayTrase/Api/JsonRpc/RequestTransformation.php +++ b/src/ScayTrase/Api/JsonRpc/RequestTransformation.php @@ -10,7 +10,7 @@ use ScayTrase\Api\Rpc\RpcRequestInterface; /** @internal */ -class RequestTransformation +final class RequestTransformation { /** @var RpcRequestInterface */ private $originalCall; diff --git a/src/ScayTrase/Api/JsonRpc/ResponseBodyValidator.php b/src/ScayTrase/Api/JsonRpc/ResponseBodyValidator.php index 7b377cf..6fc245c 100644 --- a/src/ScayTrase/Api/JsonRpc/ResponseBodyValidator.php +++ b/src/ScayTrase/Api/JsonRpc/ResponseBodyValidator.php @@ -13,10 +13,10 @@ final class ResponseBodyValidator { /** - * @param \StdClass $response + * @param \stdClass $response * @throws ResponseParseException */ - public function validate(\StdClass $response) + public function validate(\stdClass $response) { if (property_exists($response, JsonRpcResponseInterface::ERROR_FIELD) && property_exists($response, JsonRpcResponseInterface::RESULT_FIELD)) { throw ResponseParseException::bothErrorAndResultPresent(); @@ -43,10 +43,10 @@ public function validate(\StdClass $response) throw ResponseParseException::errorIsNotAnObject(); } - if (!property_exists($response->{JsonRpcResponseInterface::ERROR_FIELD}, JsonRpcResponseInterface::ERROR_CODE_FIELD)) { + if (!property_exists($response->{JsonRpcResponseInterface::ERROR_FIELD}, JsonRpcErrorInterface::ERROR_CODE_FIELD)) { throw ResponseParseException::noErrorCodePresent(); } - if (!property_exists($response->{JsonRpcResponseInterface::ERROR_FIELD}, JsonRpcResponseInterface::ERROR_MESSAGE_FIELD)) { + if (!property_exists($response->{JsonRpcResponseInterface::ERROR_FIELD}, JsonRpcErrorInterface::ERROR_MESSAGE_FIELD)) { throw ResponseParseException::noErrorMessagePresent(); } diff --git a/src/ScayTrase/Api/JsonRpc/SyncResponse.php b/src/ScayTrase/Api/JsonRpc/SyncResponse.php index 2303981..79bed43 100644 --- a/src/ScayTrase/Api/JsonRpc/SyncResponse.php +++ b/src/ScayTrase/Api/JsonRpc/SyncResponse.php @@ -11,17 +11,17 @@ final class SyncResponse implements JsonRpcResponseInterface { - /** @var \StdClass */ + /** @var \stdClass */ private $response; /** @var JsonRpcError */ private $error; /** * SyncResponse constructor. - * @param \StdClass $response + * @param \stdClass $response * @throws ResponseParseException on creating response for notification */ - public function __construct(\StdClass $response) + public function __construct(\stdClass $response) { $this->response = $response; @@ -45,13 +45,13 @@ public function getError() $rawError = $this->response->error; $data = null; - if (property_exists($rawError, JsonRpcResponseInterface::ERROR_DATA_FIELD)) { + if (property_exists($rawError, JsonRpcErrorInterface::ERROR_DATA_FIELD)) { $data = $rawError->data; } $this->error = new JsonRpcError( - $rawError->{JsonRpcResponseInterface::ERROR_CODE_FIELD}, - $rawError->{JsonRpcResponseInterface::ERROR_MESSAGE_FIELD}, + $rawError->{JsonRpcErrorInterface::ERROR_CODE_FIELD}, + $rawError->{JsonRpcErrorInterface::ERROR_MESSAGE_FIELD}, $data ); @@ -74,17 +74,36 @@ public function getBody() return $this->response->result; } - /** - * @return string JSON-RPC version - */ + /** {@inheritdoc} */ public function getVersion() { return $this->response->jsonrpc; } - /** @return string|null Response ID or null for notification pseudo-response */ + /** {@inheritdoc} */ public function getId() { return $this->response->id; } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() + { + $result = [ + self::VERSION_FIELD => JsonRpcClient::VERSION, + self::ID_FIELD => $this->getId(), + ]; + + if ($this->isSuccessful()) { + $result[self::RESULT_FIELD] = $this->getBody(); + } + + if (!$this->isSuccessful()) { + $result[self::ERROR_FIELD] = $this->getError(); + } + + return $result; + } } diff --git a/src/ScayTrase/Api/JsonRpc/Tests/JsonRpcClientTest.php b/src/ScayTrase/Api/JsonRpc/Tests/JsonRpcClientTest.php index 0b18414..14e69ed 100644 --- a/src/ScayTrase/Api/JsonRpc/Tests/JsonRpcClientTest.php +++ b/src/ScayTrase/Api/JsonRpc/Tests/JsonRpcClientTest.php @@ -8,12 +8,15 @@ namespace ScayTrase\Api\JsonRpc\Tests; use GuzzleHttp\Client; +use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\ConnectException; -use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\HandlerStack; +use GuzzleHttp\Promise\Promise; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Psr7\Uri; +use Prophecy\Argument; +use Psr\Http\Message\RequestInterface; use ScayTrase\Api\JsonRpc\JsonRpcClient; use ScayTrase\Api\JsonRpc\JsonRpcError; use ScayTrase\Api\JsonRpc\JsonRpcErrorInterface; @@ -24,6 +27,79 @@ class JsonRpcClientTest extends AbstractJsonRpcClientTest { + public function paramsProvider() + { + return [ + 'Null' => [null], + 'Empty array' => [[]], + 'Scalar' => [5], // This really shoud be an exception + 'Named Params' => [(object)['test' => 'test']], + 'Positional params' => [['test']], + ]; + } + + /** + * @dataProvider paramsProvider + * @param $params + */ + public function testSingleRequestFormatting($params) + { + $client = $this->getProphetClient($params, false); + $client->invoke(new JsonRpcRequest('test', $params, 'test')); + } + + /** + * @param mixed $params + * @param bool $isArray + * @return JsonRpcClient + */ + private function getProphetClient($params, $isArray) + { + $guzzle = $this->prophesize(ClientInterface::class); + $self = $this; + $guzzle->sendAsync(Argument::type(RequestInterface::class))->will( + function ($args) use ($self, $params, $isArray) { + /** @var RequestInterface $request */ + $request = $args[0]; + $content = $request->getBody()->getContents(); + $data = json_decode($content); + if ($isArray) { + self::assertTrue(is_array($data)); + self::assertNotEmpty($data); + $data = array_shift($data); + } + $self::assertEquals(JSON_ERROR_NONE, json_last_error()); + $self::assertObjectHasAttribute('id', $data); + $self::assertObjectHasAttribute('method', $data); + $self::assertObjectHasAttribute('params', $data); + $self::assertObjectHasAttribute('jsonrpc', $data); + $self::assertEquals('test', $data->id); + $self::assertEquals('2.0', $data->jsonrpc); + $self::assertEquals('test', $data->method); + $self::assertEquals($params, $data->params); + + return new Promise( + function () { + }, + function () { + } + ); + } + ); + $client = new JsonRpcClient($guzzle->reveal(), new Uri('http://localhost/')); + return $client; + } + + /** + * @dataProvider paramsProvider + * @param $params + */ + public function testBatchRequestFormatting($params) + { + $client = $this->getProphetClient($params, true); + $client->invoke([new JsonRpcRequest('test', $params, 'test')]); + } + public function testSingleSuccessfulRequest() { $client = new JsonRpcClient($this->getClient(), new Uri('http://localhost/')); @@ -116,7 +192,7 @@ public function testMultipleMixedRequests() self::assertEquals(JsonRpcErrorInterface::INTERNAL_ERROR, $eResponse->getError()->getCode()); } - /** @expectedException \GuzzleHttp\Exception\GuzzleException*/ + /** @expectedException \GuzzleHttp\Exception\GuzzleException */ public function testFailedCall() { $client = new JsonRpcClient($this->getClient(), new Uri('http://localhost/')); diff --git a/src/ScayTrase/Api/JsonRpc/Tests/ResponseBodyValidatorTest.php b/src/ScayTrase/Api/JsonRpc/Tests/ResponseBodyValidatorTest.php index 7a25602..3d3c766 100644 --- a/src/ScayTrase/Api/JsonRpc/Tests/ResponseBodyValidatorTest.php +++ b/src/ScayTrase/Api/JsonRpc/Tests/ResponseBodyValidatorTest.php @@ -37,22 +37,22 @@ public function validResponseBodyProvider() } /** - * @param \StdClass $body + * @param \stdClass $body * *@dataProvider invalidResponseBodyProvider * @expectedException \ScayTrase\Api\JsonRpc\Exception\ResponseParseException */ - public function testInvalidBody(\StdClass $body) + public function testInvalidBody(\stdClass $body) { $parser = new ResponseBodyValidator(); $parser->validate($body); } /** - * @param \StdClass $body + * @param \stdClass $body * @dataProvider validResponseBodyProvider */ - public function testValidBody(\StdClass $body) + public function testValidBody(\stdClass $body) { $parser = new ResponseBodyValidator(); $parser->validate($body); diff --git a/src/ScayTrase/Api/Rpc/RpcRequestInterface.php b/src/ScayTrase/Api/Rpc/RpcRequestInterface.php index 85ea7ce..a89d0d2 100644 --- a/src/ScayTrase/Api/Rpc/RpcRequestInterface.php +++ b/src/ScayTrase/Api/Rpc/RpcRequestInterface.php @@ -12,6 +12,6 @@ interface RpcRequestInterface /** @return string */ public function getMethod(); - /** @return \StdClass|\StdClass[]|null */ + /** @return \stdClass|array|null */ public function getParameters(); } diff --git a/src/ScayTrase/Api/Rpc/RpcResponseInterface.php b/src/ScayTrase/Api/Rpc/RpcResponseInterface.php index fdfca9d..699b871 100644 --- a/src/ScayTrase/Api/Rpc/RpcResponseInterface.php +++ b/src/ScayTrase/Api/Rpc/RpcResponseInterface.php @@ -15,6 +15,6 @@ public function isSuccessful(); /** @return RpcErrorInterface|null */ public function getError(); - /** @return \StdClass|\StdClass[]|null */ + /** @return \stdClass|array|mixed|null */ public function getBody(); } diff --git a/src/ScayTrase/Api/Rpc/Tests/LazyClientTest.php b/src/ScayTrase/Api/Rpc/Tests/LazyClientTest.php index f77fb2c..b2e03a4 100644 --- a/src/ScayTrase/Api/Rpc/Tests/LazyClientTest.php +++ b/src/ScayTrase/Api/Rpc/Tests/LazyClientTest.php @@ -50,7 +50,7 @@ public function testLazyRequets() self::assertTrue($c1->isFrozen()); self::assertEquals($response, $responses[$id]); self::assertTrue($response->isSuccessful()); - self::assertInstanceOf(\StdClass::class, $response->getBody()); + self::assertInstanceOf(\stdClass::class, $response->getBody()); self::assertEquals($request->getParameters(), $response->getBody()); } } From 609a8022720a47f44fee916feaf340bbc961e1ae Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Wed, 13 Apr 2016 08:26:09 +0300 Subject: [PATCH 2/2] Fix transformation --- src/ScayTrase/Api/JsonRpc/JsonRpcClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ScayTrase/Api/JsonRpc/JsonRpcClient.php b/src/ScayTrase/Api/JsonRpc/JsonRpcClient.php index e619971..187f698 100644 --- a/src/ScayTrase/Api/JsonRpc/JsonRpcClient.php +++ b/src/ScayTrase/Api/JsonRpc/JsonRpcClient.php @@ -74,7 +74,7 @@ public function invoke($calls) $this->client->sendAsync( $this->createHttpRequest($transformedCall) ), - [$transformedCall] + [new RequestTransformation($calls, $transformedCall)] ); }