Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
9 changes: 6 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@
},
"require": {
"php": ">7.0",
"yoanm/jsonrpc-server-sdk": "^1.0",
"symfony/http-foundation": "^3.0 || ^4.0"
"yoanm/jsonrpc-server-sdk": "^1.2",
"symfony/http-foundation": "^3.0 || ^4.0",
"symfony/dependency-injection": "^3.0 || ^4.0",
"yoanm/jsonrpc-server-sdk-psr11-resolver": "^2.0"
},
"require-dev": {
"behat/behat": "~3.0",
"squizlabs/php_codesniffer": "3.*",
"phpunit/phpunit": "^6.0"
"phpunit/phpunit": "^6.0 || ^7.0",
"matthiasnoback/symfony-dependency-injection-test": "^2.0 || ^3.0"
}
}
32 changes: 32 additions & 0 deletions features/bootstrap/App/CustomMethodResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
namespace Tests\Functional\BehatContext\App;

use Yoanm\JsonRpcServer\Domain\Model\JsonRpcMethodInterface;
use Yoanm\JsonRpcServer\Domain\Model\MethodResolverInterface;

class CustomMethodResolver implements MethodResolverInterface
{
/** @var JsonRpcMethodInterface[] */
private $methodList;

/**
* {@inheritdoc}
*/
public function resolve(string $methodName)
{
if (!array_key_exists($methodName, $this->methodList)) {
return null;
}

return $this->methodList[$methodName];
}

/**
* @param JsonRpcMethodInterface $method
* @param string $methodName
*/
public function addMethod(JsonRpcMethodInterface $method, string $methodName)
{
$this->methodList[$methodName] = $method;
}
}
16 changes: 16 additions & 0 deletions features/bootstrap/App/JsonRpcMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
namespace Tests\Functional\BehatContext\App;

use Yoanm\JsonRpcServer\Domain\Model\JsonRpcMethodInterface;

class JsonRpcMethod implements JsonRpcMethodInterface
{
public function validateParams(array $paramList)
{
}

public function apply(array $paramList = null)
{
return 'OK';
}
}
208 changes: 208 additions & 0 deletions features/bootstrap/FeatureContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,35 @@
namespace Tests\Functional\BehatContext;

use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use PHPUnit\Framework\Assert;
use Prophecy\Argument;
use Prophecy\Prophet;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\HttpFoundation\Request;
use Tests\Functional\BehatContext\App\CustomMethodResolver;
use Tests\Functional\BehatContext\App\JsonRpcMethod;
use Yoanm\JsonRpcServer\Infra\Endpoint\JsonRpcEndpoint;
use Yoanm\SymfonyJsonRpcHttpServer\Infra\Endpoint\JsonRpcHttpEndpoint;
use Yoanm\SymfonyJsonRpcHttpServer\Infra\Symfony\DependencyInjection\JsonRpcHttpServerExtension;

/**
* Defines application features from the specific context.
*/
class FeatureContext implements Context
{
const CUSTOM_METHOD_RESOLVER_SERVICE_ID = 'custom-method-resolver-service';

/** @var JsonRpcHttpServerExtension */
private $extension;
/** @var Prophet */
private $prophet;
/** @var ContainerBuilder */
private $containerBuilder;
/** @var mixed */
private $endpoint;
/**
* Initializes context.
*
Expand All @@ -17,5 +40,190 @@ class FeatureContext implements Context
*/
public function __construct()
{
$this->prophet = new Prophet();
}

/**
* @Given I process the symfony extension
*/
public function givenIProcessTheSymfonyExtension()
{
(new JsonRpcHttpServerExtension())->load([], $this->getContainerBuilder());
}

/**
* @Given there is a public :serviceName JSON-RPC method service
*/
public function givenThereAJsonRpcMethodService($serviceId)
{
$this->getContainerBuilder()->setDefinition($serviceId, $this->createJsonRpcMethodDefinition());
}

/**
* @Given I inject my :methodName to :serviceName JSON-RPC mapping into default method resolver definition
*/
public function givenIInjectMyJsonRpcMethodIntoDefaultMethodResolverDefinition($methodName, $serviceName)
{
$this->injectJsonRpcMethodToDefaultResolverService($methodName, $serviceName, true);
}

/**
* @Given I inject my :methodName to :serviceName JSON-RPC mapping into default method resolver instance
*/
public function givenIInjectMyJsonRpcMethodIntoDefaultMethodResolverInstance($methodName, $serviceName)
{
$this->injectJsonRpcMethodToDefaultResolverService($methodName, $serviceName);
}

/**
* @Given I tag my custom method resolver service with :tagName
*/
public function givenITagMyCustomMethodResolverServiceWith($tagName)
{
$this->getContainerBuilder()->findDefinition(self::CUSTOM_METHOD_RESOLVER_SERVICE_ID)->addTag($tagName);
}

/**
* @Given I inject my :methodName JSON-RPC method into my custom method resolver instance
*/
public function givenIInjectMyJsonRpcMethodIntoMyCustomMethodResolverInstance($methodName)
{
$this->injectJsonRpcMethodToCustomResolverService($methodName, $this->createJsonRpcMethod());
}

/**
* @Given I inject my :methodName JSON-RPC method into my custom method resolver definition
*/
public function givenIInjectMyJsonRpcMethodIntoMyCustomMethodResolverDefinition($methodName)
{
$this->injectJsonRpcMethodToCustomResolverService($methodName, $this->createJsonRpcMethodDefinition());
}

/**
* @Given I have a JSON-RPC method service definition with :tagName tag and following tag attributes:
*/
public function givenITagMyJsonRpcMethodServiceWithTagAndFollowingAttributes(
$tagName,
PyStringNode $tagAttributeNode
) {
$definition = $this->createJsonRpcMethodDefinition()
->addTag($tagName, json_decode($tagAttributeNode, true));
$this->getContainerBuilder()->setDefinition(uniqid(), $definition);
}

/**
* @When I load endpoint from :serviceId service
*/
public function whenILoadEndpointFromService($serviceId)
{
$this->getContainerBuilder()->compile();
$this->endpoint = $this->getContainerBuilder()->get($serviceId);
}

/**
* @Then endpoint should respond to following JSON-RPC methods:
*/
public function thenEndpointShouldResponseToFollowingJsonRpcMethods(TableNode $methodList)
{
Assert::assertInstanceOf(JsonRpcHttpEndpoint::class, $this->endpoint);
$methodList = array_map('array_shift', $methodList->getRows());
$this->assertEndpointRespondToCalls($this->endpoint, $methodList);
}

/**
* @param JsonRpcEndpoint $endpoint
* @param array $methodNameList
*/
private function assertEndpointRespondToCalls(JsonRpcHttpEndpoint $endpoint, array $methodNameList)
{
foreach ($methodNameList as $methodName) {
$requestId = uniqid();
$requestContent = json_encode(
[
'jsonrpc' => '2.0',
'id' => $requestId,
'method' => $methodName
]
);
$request = new Request([], [], [], [], [], [], $requestContent);
$request->setMethod(Request::METHOD_POST);
Assert::assertSame(
json_encode(
[
'jsonrpc' => '2.0',
'id' => $requestId,
'result' => 'OK'
]
),
$endpoint->index($request)->getContent()
);
}
}

/**
* @return JsonRpcMethod
*/
private function createJsonRpcMethod()
{
return new JsonRpcMethod();
}

/**
* @return Definition
*/
private function createJsonRpcMethodDefinition()
{
return (new Definition(JsonRpcMethod::class))->setPrivate(false);
}

/**
* @param string $methodName
* @param string $methodServiceId
* @param bool|false $isDefinition
*/
private function injectJsonRpcMethodToDefaultResolverService($methodName, $methodServiceId, $isDefinition = false)
{
$resolverServiceId = JsonRpcHttpServerExtension::SERVICE_NAME_RESOLVER_SERVICE_NAME;
if (true === $isDefinition) {
$this->getContainerBuilder()
->getDefinition($resolverServiceId)
->addMethodCall('addMethodMapping', [$methodName, $methodServiceId]);
} else {
$this->getContainerBuilder()
->get($resolverServiceId)
->addMethodMapping($methodName, $methodServiceId);
}
}

/**
* @param string $methodName
* @param JsonRpcMethod|Definition $method
*/
private function injectJsonRpcMethodToCustomResolverService($methodName, $method)
{
$resolverServiceId = self::CUSTOM_METHOD_RESOLVER_SERVICE_ID;
if ($method instanceof Definition) {
$this->getContainerBuilder()
->getDefinition($resolverServiceId)
->addMethodCall('addMethod', [$method, $methodName]);
} else {
$this->getContainerBuilder()
->get($resolverServiceId)
->addMethod($method, $methodName);
}
}

/**
* @return ContainerBuilder
*/
private function getContainerBuilder()
{
if (!$this->containerBuilder) {
$this->containerBuilder = new ContainerBuilder();
// Add definition of custom resolver (without tags)
$customResolverDefinition = (new Definition(CustomMethodResolver::class))->setPrivate(false);
$this->containerBuilder->setDefinition(self::CUSTOM_METHOD_RESOLVER_SERVICE_ID, $customResolverDefinition);
}
return $this->containerBuilder;
}
}
83 changes: 83 additions & 0 deletions features/symfony-extension.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
@symfony-extension
Feature: Symfony extension

Scenario: An endpoint should be quickly usable
Given I process the symfony extension
And there is a public "my_service.method.method_name" JSON-RPC method service
And there is a public "my_service.method.another" JSON-RPC method service
And there is a public "my_service.method.dummy" JSON-RPC method service
When I load endpoint from "yoanm.jsonrpc_http_server.endpoint" service
And I inject my "my-method-name" to "my_service.method.method_name" JSON-RPC mapping into default method resolver instance
# Bind service a second time
And I inject my "my-method-alias" to "my_service.method.method_name" JSON-RPC mapping into default method resolver instance
And I inject my "an-another-method" to "my_service.method.another" JSON-RPC mapping into default method resolver instance
And I inject my "getDummy" to "my_service.method.dummy" JSON-RPC mapping into default method resolver instance
Then endpoint should respond to following JSON-RPC methods:
| getDummy |
| my-method-name |
| an-another-method |
| my-method-alias |

Scenario: An endpoint should be quickly usable also by using container injection
Given I process the symfony extension
And there is a public "my_service.method.method_name" JSON-RPC method service
And there is a public "my_service.method.another" JSON-RPC method service
And there is a public "my_service.method.dummy" JSON-RPC method service
And I inject my "my-method-name" to "my_service.method.method_name" JSON-RPC mapping into default method resolver definition
# Bind service a second time
And I inject my "my-method-alias" to "my_service.method.method_name" JSON-RPC mapping into default method resolver definition
And I inject my "an-another-method" to "my_service.method.another" JSON-RPC mapping into default method resolver definition
And I inject my "getDummy" to "my_service.method.dummy" JSON-RPC mapping into default method resolver definition
When I load endpoint from "yoanm.jsonrpc_http_server.endpoint" service
Then endpoint should respond to following JSON-RPC methods:
| getDummy |
| my-method-name |
| an-another-method |
| my-method-alias |

@symfony-method-resolver-tag
Scenario: Use a custom method resolver
Given I tag my custom method resolver service with "yoanm.jsonrpc_http_server.method_resolver"
And I process the symfony extension
When I load endpoint from "yoanm.jsonrpc_http_server.endpoint" service
And I inject my "doSomething" JSON-RPC method into my custom method resolver instance
And I inject my "doAnotherThing" JSON-RPC method into my custom method resolver instance
And I inject my "doALastThing" JSON-RPC method into my custom method resolver instance
Then endpoint should respond to following JSON-RPC methods:
| doAnotherThing |
| doALastThing |
| doSomething |

@symfony-method-resolver-tag
Scenario: Use a custom method resolver with json-rpc methods autoloading
Given I tag my custom method resolver service with "yoanm.jsonrpc_http_server.method_resolver"
And I process the symfony extension
And I inject my "doSomething" JSON-RPC method into my custom method resolver definition
And I inject my "doAnotherThing" JSON-RPC method into my custom method resolver definition
And I inject my "doALastThing" JSON-RPC method into my custom method resolver definition
When I load endpoint from "yoanm.jsonrpc_http_server.endpoint" service
Then endpoint should respond to following JSON-RPC methods:
| doAnotherThing |
| doALastThing |
| doSomething |

@symfony-jsonrpc-method-tag
Scenario: Define json-rpc method with tags
Given I have a JSON-RPC method service definition with "yoanm.jsonrpc_http_server.jsonrpc_method" tag and following tag attributes:
"""
{"method": "my-method-name"}
"""
And I have a JSON-RPC method service definition with "yoanm.jsonrpc_http_server.jsonrpc_method" tag and following tag attributes:
"""
{"method": "an-another-method"}
"""
And I have a JSON-RPC method service definition with "yoanm.jsonrpc_http_server.jsonrpc_method" tag and following tag attributes:
"""
{"method": "getDummy"}
"""
And I process the symfony extension
When I load endpoint from "yoanm.jsonrpc_http_server.endpoint" service
Then endpoint should respond to following JSON-RPC methods:
| getDummy |
| my-method-name |
| an-another-method |
Empty file removed src/.gitkeep
Empty file.
8 changes: 8 additions & 0 deletions src/Infra/Endpoint/JsonRpcHttpEndpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,19 @@ class JsonRpcHttpEndpoint
/** @var SdkJsonRpcEndpoint */
private $sdkEndpoint;

/**
* @param SDKJsonRpcEndpoint $sdkEndpoint
*/
public function __construct(SDKJsonRpcEndpoint $sdkEndpoint)
{
$this->sdkEndpoint = $sdkEndpoint;
}

/**
* @param Request $request
*
* @return Response
*/
public function index(Request $request) : Response
{
if (Request::METHOD_POST !== $request->getMethod()) {
Expand Down
Loading