Skip to content

Commit 3dffde3

Browse files
committed
Pre-release fixes (#12)
* Pre-release fixes * Review API, add some documentation * Add tests on JSON-RPC request formatting * Fix transformation
1 parent 267c88c commit 3dffde3

20 files changed

+387
-132
lines changed

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,17 @@
22

33
# RPC Library
44

5-
## JSON-RPC Implementation
5+
Built-in support for batch-like requests. Processing depends on client implementation (may not be real batch)
6+
7+
## Common interfaces
8+
* RPC request (call)
9+
* RPC response (result)
10+
* RPC error
11+
## Decorators
12+
* Lazy client decorator
13+
* Loggable client decorator
14+
* Cacheable client decorator
15+
16+
# JSON-RPC Implementation
17+
18+
[JSON-RPC 2.0 Specification](http://www.jsonrpc.org/specification)

composer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
"minimum-stability": "stable",
1212
"require": {
1313
"php": "~5.5|~7.0",
14+
"psr/log": "~1.0",
1415
"guzzlehttp/guzzle": "~6.0",
1516
"paragonie/random_compat": "~1.1@stable"
1617
},
1718
"require-dev": {
1819
"phpunit/phpunit": "~4.5|~5.1",
19-
"psr/log": "~1.0",
2020
"psr/cache": "~1.0"
2121
},
2222
"autoload": {
@@ -25,7 +25,6 @@
2525
}
2626
},
2727
"suggest": {
28-
"psr/log": "For LoggableRpcClient decorator",
2928
"psr/cache": "For CacheableRpcClient decorator"
3029
}
3130
}

src/ScayTrase/Api/JsonRpc/JsonRpcClient.php

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
use GuzzleHttp\Exception\GuzzleException;
1212
use GuzzleHttp\Psr7\Request;
1313
use Psr\Http\Message\UriInterface;
14+
use Psr\Log\LoggerInterface;
15+
use Psr\Log\NullLogger;
1416
use ScayTrase\Api\IdGenerator\IdGeneratorInterface;
1517
use ScayTrase\Api\IdGenerator\UuidGenerator;
1618
use ScayTrase\Api\Rpc\Exception\RemoteCallFailedException;
@@ -29,44 +31,76 @@ final class JsonRpcClient implements RpcClientInterface
2931
private $uri;
3032
/** @var IdGeneratorInterface */
3133
private $idGenerator;
34+
/** @var LoggerInterface */
35+
private $logger;
3236

3337
/**
3438
* JsonRpcClient constructor.
3539
* @param ClientInterface $client
3640
* @param UriInterface $endpoint
3741
* @param IdGeneratorInterface|null $idGenerator
42+
* @param LoggerInterface $logger
3843
*/
39-
public function __construct(ClientInterface $client, UriInterface $endpoint, IdGeneratorInterface $idGenerator = null)
44+
public function __construct(
45+
ClientInterface $client,
46+
UriInterface $endpoint,
47+
IdGeneratorInterface $idGenerator = null,
48+
LoggerInterface $logger = null
49+
)
4050
{
4151
$this->client = $client;
4252
$this->uri = $endpoint;
4353
$this->idGenerator = $idGenerator;
54+
$this->logger = $logger;
4455

4556
if (null === $this->idGenerator) {
4657
$this->idGenerator = new UuidGenerator();
4758
}
59+
60+
if (null === $this->logger) {
61+
$this->logger = new NullLogger();
62+
}
4863
}
4964

5065
/**
5166
* {@inheritdoc}
5267
*/
5368
public function invoke($calls)
5469
{
55-
if (!is_array($calls) && ($calls instanceof RpcRequestInterface)) {
56-
$calls = [$calls];
57-
}
70+
try {
71+
if (!is_array($calls) && ($calls instanceof RpcRequestInterface)) {
72+
$transformedCall = $this->transformCall($calls);
73+
return new JsonRpcResponseCollection(
74+
$this->client->sendAsync(
75+
$this->createHttpRequest($transformedCall)
76+
),
77+
[new RequestTransformation($calls, $transformedCall)]
78+
);
79+
}
80+
81+
$requests = [];
82+
$batchRequest = [];
5883

59-
$requests = [];
60-
$requestBody = [];
84+
foreach ($calls as $key => $call) {
85+
$transformedCall = $this->transformCall($call);
86+
$requests[spl_object_hash($call)] = new RequestTransformation($call, $transformedCall);
87+
$batchRequest[] = $transformedCall;
88+
}
6189

62-
foreach ($calls as $key => $call) {
63-
$transformedCall = $this->transformCall($call);
64-
$requests[spl_object_hash($call)] = new RequestTransformation($call, $transformedCall);
65-
$requestBody[] = $this->formatJsonRpcCall($transformedCall);
90+
return new JsonRpcResponseCollection($this->client->sendAsync($this->createHttpRequest($batchRequest)), $requests);
91+
} catch (GuzzleException $exception) {
92+
throw new RemoteCallFailedException($exception->getMessage(), 0, $exception);
6693
}
94+
}
6795

96+
/**
97+
* @param $requestBody
98+
* @return Request
99+
*/
100+
private function createHttpRequest($requestBody)
101+
{
68102
/** @noinspection ExceptionsAnnotatingAndHandlingInspection */
69-
$request = new Request(
103+
return new Request(
70104
'POST',
71105
$this->uri,
72106
[
@@ -75,12 +109,6 @@ public function invoke($calls)
75109
],
76110
json_encode($requestBody, JSON_PRETTY_PRINT)
77111
);
78-
79-
try {
80-
return new JsonRpcResponseCollection($this->client->sendAsync($request), $requests);
81-
} catch (GuzzleException $exception) {
82-
throw new RemoteCallFailedException($exception->getMessage(), 0, $exception);
83-
}
84112
}
85113

86114
/**
@@ -96,23 +124,4 @@ private function transformCall(RpcRequestInterface $call)
96124
}
97125
return $transformedCall;
98126
}
99-
100-
/**
101-
* @param JsonRpcRequestInterface|RpcRequestInterface $request
102-
* @return array
103-
*/
104-
private function formatJsonRpcCall(JsonRpcRequestInterface $request)
105-
{
106-
$result = [
107-
'jsonrpc' => static::VERSION,
108-
'method' => $request->getMethod(),
109-
'params' => $request->getParameters(),
110-
];
111-
112-
if (!$request->isNotification()) {
113-
$result['id'] = $request->getId();
114-
}
115-
116-
return $result;
117-
}
118127
}

src/ScayTrase/Api/JsonRpc/JsonRpcError.php

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,37 +13,52 @@ final class JsonRpcError implements JsonRpcErrorInterface
1313
private $code;
1414
/** @var string */
1515
private $message;
16-
/** @var null|\StdClass */
16+
/** @var null|\stdClass */
1717
private $data;
1818

1919
/**
2020
* JsonRpcError constructor.
2121
* @param int $code
2222
* @param string $message
23-
* @param \StdClass|null $data
23+
* @param \stdClass|mixed|null $data
2424
*/
25-
public function __construct($code, $message, \StdClass $data = null)
25+
public function __construct($code, $message, $data = null)
2626
{
2727
$this->code = $code;
2828
$this->message = $message;
2929
$this->data = $data;
3030
}
3131

32-
/** @return int */
32+
/** {@inheritdoc} */
3333
public function getCode()
3434
{
3535
return $this->code;
3636
}
3737

38-
/** @return string */
38+
/** {@inheritdoc} */
3939
public function getMessage()
4040
{
4141
return $this->message;
4242
}
4343

44-
/** @return \StdClass|null error data */
44+
/** {@inheritdoc} */
4545
public function getData()
4646
{
4747
return $this->data;
4848
}
49+
50+
/** {@inheritdoc} */
51+
public function jsonSerialize()
52+
{
53+
$error = [
54+
self::ERROR_CODE_FIELD => $this->getCode(),
55+
self::ERROR_MESSAGE_FIELD => $this->getMessage(),
56+
];
57+
58+
if (null !== ($data = $this->getData())) {
59+
$error[self::ERROR_DATA_FIELD] = $data;
60+
}
61+
62+
return $error;
63+
}
4964
}

src/ScayTrase/Api/JsonRpc/JsonRpcErrorInterface.php

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,46 @@
99

1010
use ScayTrase\Api\Rpc\RpcErrorInterface;
1111

12-
interface JsonRpcErrorInterface extends RpcErrorInterface
12+
interface JsonRpcErrorInterface extends RpcErrorInterface, \JsonSerializable
1313
{
14+
const ERROR_CODE_FIELD = 'code';
15+
const ERROR_MESSAGE_FIELD = 'message';
16+
const ERROR_DATA_FIELD = 'data';
17+
1418
CONST PARSE_ERROR = -32700;
1519
CONST INVALID_REQUEST = -32600;
1620
CONST METHOD_NOT_FOUND = -32601;
1721
CONST INVALID_PARAMS = -32602;
1822
CONST INTERNAL_ERROR = -32603;
1923

20-
/** @return \StdClass|null error data */
24+
/**
25+
* Returns error code
26+
*
27+
* A Number that indicates the error type that occurred.
28+
* This MUST be an integer.
29+
*
30+
* @return int
31+
*/
32+
public function getCode();
33+
34+
/**
35+
* Return error message
36+
*
37+
* String providing a short description of the error.
38+
* The message SHOULD be limited to a concise single sentence.
39+
*
40+
* @return string
41+
*/
42+
public function getMessage();
43+
44+
/**
45+
* Returns amy additional error information specified by server
46+
*
47+
* A Primitive or Structured value that contains additional information about the error.
48+
* This may be omitted.
49+
* The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.).
50+
*
51+
* @return \stdClass|mixed|null error data
52+
*/
2153
public function getData();
2254
}

src/ScayTrase/Api/JsonRpc/JsonRpcNotification.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ final class JsonRpcNotification implements JsonRpcRequestInterface
1111
{
1212
/** @var string */
1313
private $method;
14-
/** @var array */
14+
/** @var \stdClass|array|null */
1515
private $parameters;
1616

1717
/**
1818
* JsonRpcNotificationRequest constructor.
1919
* @param string $method
20-
* @param array $parameters
20+
* @param \stdClass|array|null $parameters
2121
*/
22-
public function __construct($method, array $parameters)
22+
public function __construct($method, $parameters)
2323
{
2424
$this->method = $method;
2525
$this->parameters = $parameters;
@@ -48,4 +48,20 @@ public function getParameters()
4848
{
4949
return $this->parameters;
5050
}
51+
52+
/** {@inheritdoc} */
53+
public function getVersion()
54+
{
55+
return JsonRpcClient::VERSION;
56+
}
57+
58+
/** {@inheritdoc} */
59+
public function jsonSerialize()
60+
{
61+
return [
62+
self::VERSION_FIELD => JsonRpcClient::VERSION,
63+
self::METHOD_FIELD => $this->getMethod(),
64+
self::PARAMETERS_FIELD => $this->getParameters(),
65+
];
66+
}
5167
}

src/ScayTrase/Api/JsonRpc/JsonRpcNotificationResponse.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,13 @@ public function getId()
3838
{
3939
return null;
4040
}
41+
42+
/**
43+
* {@inheritdoc}
44+
* @throws \LogicException
45+
*/
46+
public function jsonSerialize()
47+
{
48+
throw new \LogicException('Notification should not have transferable response representation');
49+
}
4150
}

src/ScayTrase/Api/JsonRpc/JsonRpcRequest.php

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ final class JsonRpcRequest implements JsonRpcRequestInterface
1515
private $id;
1616
/** @var string */
1717
private $method;
18-
/** @var \StdClass|\StdClass[]|null */
18+
/** @var \stdClass|array|null */
1919
private $parameters;
2020

2121
/**
2222
* JsonRpcRequest constructor.
2323
* @param string $method
24-
* @param \StdClass|\StdClass[]|null $parameters
24+
* @param \stdClass|array|null $parameters
2525
* @param string $id
2626
*/
2727
public function __construct($method, $parameters = null, $id = null)
@@ -64,4 +64,34 @@ public function getParameters()
6464
{
6565
return $this->parameters;
6666
}
67+
68+
/**
69+
* {@inheritdoc}
70+
*/
71+
public function jsonSerialize()
72+
{
73+
$result = [
74+
self::VERSION_FIELD => JsonRpcClient::VERSION,
75+
self::METHOD_FIELD => $this->getMethod(),
76+
self::PARAMETERS_FIELD => $this->getParameters(),
77+
];
78+
79+
if (!$this->isNotification()) {
80+
$result[self::ID_FIELD] = $this->getId();
81+
}
82+
83+
return $result;
84+
}
85+
86+
/**
87+
* Returns version of the JSON-RPC request
88+
*
89+
* A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0".
90+
*
91+
* @return string JSON-RPC version
92+
*/
93+
public function getVersion()
94+
{
95+
return JsonRpcClient::VERSION;
96+
}
6797
}

0 commit comments

Comments
 (0)