Skip to content

Commit

Permalink
Pre-release fixes (#12)
Browse files Browse the repository at this point in the history
* Pre-release fixes

* Review API, add some documentation
* Add tests on JSON-RPC request formatting

* Fix transformation
  • Loading branch information
scaytrase committed Apr 13, 2016
1 parent 267c88c commit 3dffde3
Show file tree
Hide file tree
Showing 20 changed files with 387 additions and 132 deletions.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand All @@ -25,7 +25,6 @@
}
},
"suggest": {
"psr/log": "For LoggableRpcClient decorator",
"psr/cache": "For CacheableRpcClient decorator"
}
}
81 changes: 45 additions & 36 deletions src/ScayTrase/Api/JsonRpc/JsonRpcClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,44 +31,76 @@ 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();
}
}

/**
* {@inheritdoc}
*/
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)
),
[new RequestTransformation($calls, $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,
[
Expand All @@ -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);
}
}

/**
Expand All @@ -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;
}
}
27 changes: 21 additions & 6 deletions src/ScayTrase/Api/JsonRpc/JsonRpcError.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
36 changes: 34 additions & 2 deletions src/ScayTrase/Api/JsonRpc/JsonRpcErrorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
22 changes: 19 additions & 3 deletions src/ScayTrase/Api/JsonRpc/JsonRpcNotification.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(),
];
}
}
9 changes: 9 additions & 0 deletions src/ScayTrase/Api/JsonRpc/JsonRpcNotificationResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
}
34 changes: 32 additions & 2 deletions src/ScayTrase/Api/JsonRpc/JsonRpcRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
}
Loading

0 comments on commit 3dffde3

Please sign in to comment.