From 544238ae0070a6ef110f23a9e6bb7c4b2d165860 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Fri, 23 Jun 2017 14:38:47 +0300 Subject: [PATCH 01/29] handle security annotations --- .travis.yml | 28 +++++--- Adapters/JMS/HandledType.php | 27 ++++++++ .../JMS}/HandledTypeDriver.php | 2 +- Adapters/JMS/Relation.php | 7 ++ .../JMS}/RelationsHandler.php | 42 ++++++------ .../SerializedJsonRpcResponseListener.php | 3 +- BankiruJsonRpcServerBundle.php | 25 +++++++ .../BankiruJsonRpcServerExtension.php | 25 +++++++ .../Compiler/JmsDriverPass.php | 41 ++++++++++- DependencyInjection/JsonRpcExtension.php | 68 ------------------- Http/JsonRpcHttpResponse.php | 29 ++++---- JsonRpcBundle.php | 18 ++--- Listener/AccessDeniedExceptionListener.php | 34 ++++++++++ Listener/ExceptionHandlerListener.php | 1 + Listener/HttpExceptionListener.php | 14 +--- Resources/config/security.yml | 5 ++ Serializer/HandledType.php | 26 ++----- Specification/JsonRpcRequest.php | 26 +++---- Specification/JsonRpcResponse.php | 24 +++---- Specification/RichJsonRpcRequest.php | 10 +-- .../JsonRpcTestExtension.php | 4 +- Test/JsonRpc/AnnotationController.php | 2 +- Test/JsonRpc/FailingController.php | 2 +- Test/JsonRpc/SecurityController.php | 30 ++++++++ Test/JsonRpc/TestController.php | 2 +- Test/Tests/AnnotatedControllerTest.php | 8 +-- Test/Tests/EntityConversionTest.php | 9 ++- Test/Tests/ExceptionHandlingTest.php | 10 +-- Test/Tests/Fixtures/Kernel.php | 18 ++--- Test/Tests/Fixtures/config.yml | 15 +++- Test/Tests/JsonRpcTestCase.php | 22 ++++++ Test/Tests/RouterTest.php | 4 +- Test/Tests/SampleControllerTest.php | 24 +++---- Test/Tests/SecurityTest.php | 25 +++++++ composer.json | 18 +++-- 35 files changed, 394 insertions(+), 254 deletions(-) create mode 100644 Adapters/JMS/HandledType.php rename {Serializer => Adapters/JMS}/HandledTypeDriver.php (97%) create mode 100644 Adapters/JMS/Relation.php rename {Serializer => Adapters/JMS}/RelationsHandler.php (94%) rename {Listener => Adapters/JMS}/SerializedJsonRpcResponseListener.php (98%) create mode 100644 BankiruJsonRpcServerBundle.php create mode 100644 DependencyInjection/BankiruJsonRpcServerExtension.php delete mode 100644 DependencyInjection/JsonRpcExtension.php create mode 100644 Listener/AccessDeniedExceptionListener.php create mode 100644 Resources/config/security.yml create mode 100644 Test/JsonRpc/SecurityController.php create mode 100644 Test/Tests/SecurityTest.php diff --git a/.travis.yml b/.travis.yml index 75401ab..7ee0c9e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,24 +4,29 @@ php: - 5.5 - 5.6 - 7 + - 7.1 - nightly - hhvm env: - - PACKAGES='symfony/symfony=2.7.*' - - PACKAGES='symfony/symfony=2.8.*' - - PACKAGES='symfony/symfony=3.1.*' - - PACKAGES='symfony/symfony=3.2.*' - - PACKAGES='symfony/symfony=~3.3@dev' + - PACKAGES='symfony/symfony=2.7.*' deps='no' + - PACKAGES='symfony/symfony=2.8.*' deps='no' + - PACKAGES='symfony/symfony=3.2.*' deps='no' + - PACKAGES='symfony/symfony=3.3.*' deps='no' + - PACKAGES='symfony/symfony=~3.4@dev' deps='no' matrix: - include: - - php: 5.5 - env: PACKAGES='symfony/symfony=2.7.*' deps='low' + include: + - php: 5.5 + env: PACKAGES='symfony/symfony=2.7.*' deps='low' + - php: 7.1 + env: PACKAGES='symfony/symfony=~4.0@dev sensio/framework-extra-bundle=~4.0@dev' deps='no' + - php: nightly + env: PACKAGES='symfony/symfony=~4.0@dev sensio/framework-extra-bundle=~4.0@dev' deps='no' - allow_failures: - - php: hhvm - - php: nightly + allow_failures: + - php: hhvm + - php: nightly before_install: - travis_retry composer self-update @@ -32,5 +37,6 @@ install: - if [ "$deps" = "low" ]; then composer --prefer-source --prefer-lowest --prefer-stable update; composer --prefer-source install; fi; script: + - rm -rf build - mkdir -p build - vendor/bin/phpunit --colors -c phpunit.xml diff --git a/Adapters/JMS/HandledType.php b/Adapters/JMS/HandledType.php new file mode 100644 index 0000000..24d7b74 --- /dev/null +++ b/Adapters/JMS/HandledType.php @@ -0,0 +1,27 @@ +handler = $handler; } + + /** + * @return string + */ + public function getHandler() + { + return $this->handler; + } +} diff --git a/Serializer/HandledTypeDriver.php b/Adapters/JMS/HandledTypeDriver.php similarity index 97% rename from Serializer/HandledTypeDriver.php rename to Adapters/JMS/HandledTypeDriver.php index 3d3d4fd..04dff66 100644 --- a/Serializer/HandledTypeDriver.php +++ b/Adapters/JMS/HandledTypeDriver.php @@ -1,6 +1,6 @@ manager = $manager; } - + public function __construct(EntityManagerInterface $manager) + { + $this->manager = $manager; + } public function serializeRelation(JsonSerializationVisitor $visitor, $relation, array $type, Context $context) { @@ -36,23 +38,6 @@ public function serializeRelation(JsonSerializationVisitor $visitor, $relation, return $this->getSingleEntityRelation($relation); } - /** - * @param $relation - * - * @return array|mixed - */ - protected function getSingleEntityRelation($relation) - { - $metadata = $this->manager->getClassMetadata(get_class($relation)); - - $ids = $metadata->getIdentifierValues($relation); - if (!$metadata->isIdentifierComposite) { - $ids = array_shift($ids); - } - - return $ids; - } - public function deserializeRelation(JsonDeserializationVisitor $visitor, $relation, array $type, Context $context) { $className = isset($type['params'][0]['name']) ? $type['params'][0]['name'] : null; @@ -87,4 +72,21 @@ public function deserializeRelation(JsonDeserializationVisitor $visitor, $relati return $objects; } + + /** + * @param $relation + * + * @return array|mixed + */ + private function getSingleEntityRelation($relation) + { + $metadata = $this->manager->getClassMetadata(get_class($relation)); + + $ids = $metadata->getIdentifierValues($relation); + if (!$metadata->isIdentifierComposite) { + $ids = array_shift($ids); + } + + return $ids; + } } diff --git a/Listener/SerializedJsonRpcResponseListener.php b/Adapters/JMS/SerializedJsonRpcResponseListener.php similarity index 98% rename from Listener/SerializedJsonRpcResponseListener.php rename to Adapters/JMS/SerializedJsonRpcResponseListener.php index 68f9c6f..96d85b5 100644 --- a/Listener/SerializedJsonRpcResponseListener.php +++ b/Adapters/JMS/SerializedJsonRpcResponseListener.php @@ -1,6 +1,6 @@ setResponse($response); - } /** diff --git a/BankiruJsonRpcServerBundle.php b/BankiruJsonRpcServerBundle.php new file mode 100644 index 0000000..d078b3a --- /dev/null +++ b/BankiruJsonRpcServerBundle.php @@ -0,0 +1,25 @@ +addCompilerPass(new JmsDriverPass(), PassConfig::TYPE_BEFORE_REMOVING); + } + + public function getContainerExtension() + { + return new BankiruJsonRpcServerExtension(); + } +} diff --git a/DependencyInjection/BankiruJsonRpcServerExtension.php b/DependencyInjection/BankiruJsonRpcServerExtension.php new file mode 100644 index 0000000..8941e8c --- /dev/null +++ b/DependencyInjection/BankiruJsonRpcServerExtension.php @@ -0,0 +1,25 @@ +getParameter('kernel.bundles'); + + $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); + $loader->load('jsonrpc.yml'); + + if (in_array(SecurityBundle::class, $bundles, true)) { + $loader->load('security.yml'); + } + } +} diff --git a/DependencyInjection/Compiler/JmsDriverPass.php b/DependencyInjection/Compiler/JmsDriverPass.php index de50fcf..1e0142c 100644 --- a/DependencyInjection/Compiler/JmsDriverPass.php +++ b/DependencyInjection/Compiler/JmsDriverPass.php @@ -2,7 +2,7 @@ namespace Bankiru\Api\JsonRpc\DependencyInjection\Compiler; -use Bankiru\Api\JsonRpc\Serializer\HandledTypeDriver; +use Bankiru\Api\JsonRpc\Adapters\JMS\HandledTypeDriver; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; @@ -25,5 +25,44 @@ public function process(ContainerBuilder $container) ); $container->setAlias('jms_serializer.metadata_driver', 'jms_serializer.driver.relation'); + + $container->register('jms_serializer.handler.relation', RelationsHandler::class) + ->setArguments([new Reference('doctrine.orm.entity_manager')]) + ->addTag( + 'jms_serializer.handler', + [ + 'type' => 'Relation', + 'direction' => 'serialization', + 'format' => 'json', + 'method' => 'serializeRelation', + ] + ) + ->addTag( + 'jms_serializer.handler', + [ + 'type' => 'Relation', + 'direction' => 'deserialization', + 'format' => 'json', + 'method' => 'deserializeRelation', + ] + ) + ->addTag( + 'jms_serializer.handler', + [ + 'type' => 'Relation', + 'direction' => 'serialization', + 'format' => 'json', + 'method' => 'serializeRelation', + ] + ) + ->addTag( + 'jms_serializer.handler', + [ + 'type' => 'Relation', + 'direction' => 'deserialization', + 'format' => 'json', + 'method' => 'deserializeRelation', + ] + ); } } diff --git a/DependencyInjection/JsonRpcExtension.php b/DependencyInjection/JsonRpcExtension.php deleted file mode 100644 index 3d587af..0000000 --- a/DependencyInjection/JsonRpcExtension.php +++ /dev/null @@ -1,68 +0,0 @@ -load('jsonrpc.yml'); - } - - /** {@inheritdoc} */ - public function process(ContainerBuilder $container) - { - if (!$container->has('doctrine')) { - return; - } - - $container->register('jms_serializer.handler.relation', RelationsHandler::class) - ->setArguments([new Reference('doctrine.orm.entity_manager')]) - ->addTag( - 'jms_serializer.handler', - [ - 'type' => 'Relation', - 'direction' => 'serialization', - 'format' => 'json', - 'method' => 'serializeRelation', - ] - ) - ->addTag( - 'jms_serializer.handler', - [ - 'type' => 'Relation', - 'direction' => 'deserialization', - 'format' => 'json', - 'method' => 'deserializeRelation', - ] - ) - ->addTag( - 'jms_serializer.handler', - [ - 'type' => 'Relation', - 'direction' => 'serialization', - 'format' => 'json', - 'method' => 'serializeRelation', - ] - ) - ->addTag( - 'jms_serializer.handler', - [ - 'type' => 'Relation', - 'direction' => 'deserialization', - 'format' => 'json', - 'method' => 'deserializeRelation', - ] - ); - } -} diff --git a/Http/JsonRpcHttpResponse.php b/Http/JsonRpcHttpResponse.php index 3291d46..a5848e8 100644 --- a/Http/JsonRpcHttpResponse.php +++ b/Http/JsonRpcHttpResponse.php @@ -2,7 +2,7 @@ namespace Bankiru\Api\JsonRpc\Http; -use Bankiru\Api\JsonRpc\JsonRpcBundle; +use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; use ScayTrase\Api\JsonRpc\JsonRpcErrorInterface; use ScayTrase\Api\JsonRpc\JsonRpcResponseInterface; use Symfony\Component\HttpFoundation\JsonResponse; @@ -24,18 +24,13 @@ public function __construct($jsonRpc = null, $status = 200, array $headers = []) return; } - if (!is_array($jsonRpc)) { - parent::__construct($this->formatJsonRpcResponse($jsonRpc), $status, $headers); + if (is_array($jsonRpc)) { + parent::__construct(array_map([$this, 'formatJsonRpcResponse'], $jsonRpc), $status, $headers); return; } - $data = []; - foreach ($jsonRpc as $response) { - $data[] = $this->formatJsonRpcResponse($response); - } - - parent::__construct($data, $status, $headers); + parent::__construct($this->formatJsonRpcResponse($jsonRpc), $status, $headers); } /** @@ -46,21 +41,21 @@ public function __construct($jsonRpc = null, $status = 200, array $headers = []) private function formatJsonRpcResponse(JsonRpcResponseInterface $jsonRpc) { $data = [ - 'jsonrpc' => JsonRpcBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, 'id' => $jsonRpc->getId(), ]; if ($jsonRpc->isSuccessful()) { $data['result'] = $jsonRpc->getBody(); - return $data; - } else { - $error = $jsonRpc->getError(); - $data['error']['code'] = $error->getCode(); - $data['error']['message'] = $error->getMessage(); - $data['error']['data'] = $error instanceof JsonRpcErrorInterface ? $error->getData() : null; - return $data; } + + $error = $jsonRpc->getError(); + $data['error']['code'] = $error->getCode(); + $data['error']['message'] = $error->getMessage(); + $data['error']['data'] = $error instanceof JsonRpcErrorInterface ? $error->getData() : null; + + return $data; } } diff --git a/JsonRpcBundle.php b/JsonRpcBundle.php index 554cdb9..9bcd7b0 100644 --- a/JsonRpcBundle.php +++ b/JsonRpcBundle.php @@ -2,18 +2,10 @@ namespace Bankiru\Api\JsonRpc; -use Bankiru\Api\JsonRpc\DependencyInjection\Compiler\JmsDriverPass; -use Symfony\Component\DependencyInjection\Compiler\PassConfig; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\HttpKernel\Bundle\Bundle; - -class JsonRpcBundle extends Bundle +/** + * @deprecated + * @see BankiruJsonRpcServerBundle::class + */ +final class JsonRpcBundle extends BankiruJsonRpcServerBundle { - const VERSION = '2.0'; - - public function build(ContainerBuilder $container) - { - parent::build($container); - $container->addCompilerPass(new JmsDriverPass(), PassConfig::TYPE_BEFORE_REMOVING); - } } diff --git a/Listener/AccessDeniedExceptionListener.php b/Listener/AccessDeniedExceptionListener.php new file mode 100644 index 0000000..f88907e --- /dev/null +++ b/Listener/AccessDeniedExceptionListener.php @@ -0,0 +1,34 @@ +getRequest(); + if (!$request instanceof JsonRpcRequestInterface) { + return; + } + + $exception = $event->getException(); + + if (!$exception instanceof AccessDeniedException) { + return; + } + + $event->setException( + JsonRpcException::create( + JsonRpcError::METHOD_NOT_FOUND, + $exception->getMessage(), + $exception->getTrace() + ) + ); + } +} diff --git a/Listener/ExceptionHandlerListener.php b/Listener/ExceptionHandlerListener.php index 535b5bc..768a626 100644 --- a/Listener/ExceptionHandlerListener.php +++ b/Listener/ExceptionHandlerListener.php @@ -31,6 +31,7 @@ public function onJsonRpcException(GetExceptionResponseEvent $event) } $exception = $event->getException(); + if ($exception instanceof InvalidMethodParametersException) { $exception = JsonRpcException::create( diff --git a/Listener/HttpExceptionListener.php b/Listener/HttpExceptionListener.php index 40018f8..fe9a0ca 100644 --- a/Listener/HttpExceptionListener.php +++ b/Listener/HttpExceptionListener.php @@ -6,20 +6,8 @@ use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -class HttpExceptionListener +final class HttpExceptionListener { - private $debug = false; - - /** - * ExceptionHandlerListener constructor. - * - * @param bool $debug - */ - public function __construct($debug) - { - $this->debug = (bool)$debug; - } - public function onJsonRpcException(GetResponseForExceptionEvent $event) { $exception = $event->getException(); diff --git a/Resources/config/security.yml b/Resources/config/security.yml new file mode 100644 index 0000000..1e766e1 --- /dev/null +++ b/Resources/config/security.yml @@ -0,0 +1,5 @@ +services: + jsonrpc.security.exception_listener: + class: Bankiru\Api\JsonRpc\Listener\AccessDeniedExceptionListener + tags: + - { name: kernel.event_listener, event: rpc.exception, method: onRpcException, priority: 255 } diff --git a/Serializer/HandledType.php b/Serializer/HandledType.php index e691ed8..615b658 100644 --- a/Serializer/HandledType.php +++ b/Serializer/HandledType.php @@ -2,29 +2,11 @@ namespace Bankiru\Api\JsonRpc\Serializer; +use Bankiru\Api\JsonRpc\Adapters\JMS\HandledType as BaseHandledType; + /** - * Class ApiRelation - * - * @package Bankiru\Api\ApiClientBridge\Serializer - * @Annotation - * @Target("PROPERTY") + * @deprecated */ -class HandledType +class HandledType extends BaseHandledType { - public $handler; - - /** - * HandledType constructor. - * - * @param $handler - */ - public function __construct($handler = 'Relation') { $this->handler = $handler; } - - /** - * @return string - */ - public function getHandler() - { - return $this->handler; - } } diff --git a/Specification/JsonRpcRequest.php b/Specification/JsonRpcRequest.php index f04b0bc..e886532 100644 --- a/Specification/JsonRpcRequest.php +++ b/Specification/JsonRpcRequest.php @@ -2,12 +2,12 @@ namespace Bankiru\Api\JsonRpc\Specification; +use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; use Bankiru\Api\JsonRpc\Exception\InvalidRequestException; use Bankiru\Api\JsonRpc\Exception\JsonRpcException; -use Bankiru\Api\JsonRpc\JsonRpcBundle; -use ScayTrase\Api\JsonRpc\JsonRpcError; use ScayTrase\Api\JsonRpc\JsonRpcRequestInterface; +/** @internal */ final class JsonRpcRequest implements JsonRpcRequestInterface { /** @var string|null */ @@ -17,12 +17,6 @@ final class JsonRpcRequest implements JsonRpcRequestInterface /** @var mixed|\stdClass */ private $parameters; - /** @return bool True if request should not receive response from the server */ - public function isNotification() - { - return null === $this->id; - } - /** * @param \stdClass $source * @@ -44,8 +38,8 @@ public static function fromStdClass(\stdClass $source) throw InvalidRequestException::missingFields($missing); } - if (JsonRpcBundle::VERSION !== $source->jsonrpc) { - throw InvalidRequestException::invalidVersion(JsonRpcBundle::VERSION, $source->jsonrpc); + if (BankiruJsonRpcServerBundle::VERSION !== $source->jsonrpc) { + throw InvalidRequestException::invalidVersion(BankiruJsonRpcServerBundle::VERSION, $source->jsonrpc); } $request->id = isset($source->id) ? $source->id : null; @@ -55,6 +49,12 @@ public static function fromStdClass(\stdClass $source) return $request; } + /** {@inheritdoc} */ + public function isNotification() + { + return null === $this->id; + } + /** {@inheritdoc} */ public function jsonSerialize() { @@ -69,7 +69,7 @@ public function jsonSerialize() /** {@inheritdoc} */ public function getVersion() { - return JsonRpcBundle::VERSION; + return BankiruJsonRpcServerBundle::VERSION; } /** @return string */ @@ -78,13 +78,13 @@ public function getMethod() return $this->method; } - /** @return array */ + /** {@inheritdoc} */ public function getParameters() { return $this->parameters; } - /** @return int|null Id. if not a notification and id is not set - id should be automatically generated */ + /** {@inheritdoc} */ public function getId() { return $this->id; diff --git a/Specification/JsonRpcResponse.php b/Specification/JsonRpcResponse.php index 8edaefe..5cb219c 100644 --- a/Specification/JsonRpcResponse.php +++ b/Specification/JsonRpcResponse.php @@ -2,10 +2,11 @@ namespace Bankiru\Api\JsonRpc\Specification; -use Bankiru\Api\JsonRpc\JsonRpcBundle; +use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; use ScayTrase\Api\JsonRpc\JsonRpcResponseInterface; use ScayTrase\Api\Rpc\RpcErrorInterface; +/** @internal */ final class JsonRpcResponse implements JsonRpcResponseInterface { /** @var string */ @@ -29,22 +30,17 @@ public function __construct($id = null, $body = null, RpcErrorInterface $error = $this->error = $error; } - - /** - * @return string JSON-RPC version - */ + /** {@inheritdoc} */ public function getVersion() { - return JsonRpcBundle::VERSION; + return BankiruJsonRpcServerBundle::VERSION; } - /** - * {@inheritdoc} - */ + /** {@inheritdoc} */ public function jsonSerialize() { $result = [ - self::VERSION_FIELD => JsonRpcBundle::VERSION, + self::VERSION_FIELD => BankiruJsonRpcServerBundle::VERSION, self::ID_FIELD => $this->getId(), ]; @@ -59,25 +55,25 @@ public function jsonSerialize() return $result; } - /** @return string|null Response ID or null for notification pseudo-response */ + /** {@inheritdoc} */ public function getId() { return $this->id; } - /** @return bool */ + /** {@inheritdoc} */ public function isSuccessful() { return $this->getError() === null; } - /** @return RpcErrorInterface|null */ + /** {@inheritdoc} */ public function getError() { return $this->error; } - /** @return \stdClass|\stdClass[]|null */ + /** {@inheritdoc} */ public function getBody() { return $this->body; diff --git a/Specification/RichJsonRpcRequest.php b/Specification/RichJsonRpcRequest.php index a462ee5..1a04d4d 100644 --- a/Specification/RichJsonRpcRequest.php +++ b/Specification/RichJsonRpcRequest.php @@ -2,7 +2,7 @@ namespace Bankiru\Api\JsonRpc\Specification; -use Bankiru\Api\JsonRpc\JsonRpcBundle; +use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; use Bankiru\Api\Rpc\Http\RequestInterface; use ScayTrase\Api\JsonRpc\JsonRpcRequestInterface; use Symfony\Component\HttpFoundation\ParameterBag; @@ -23,11 +23,7 @@ final class RichJsonRpcRequest implements RequestInterface, JsonRpcRequestInterf public function __construct(JsonRpcRequestInterface $request, ParameterBag $attributes = null) { $this->request = $request; - $this->attributes = $attributes; - - if (null === $this->attributes) { - $this->attributes = new ParameterBag(); - } + $this->attributes = $attributes ?: new ParameterBag(); } /** {@inheritdoc} */ @@ -56,7 +52,7 @@ public function jsonSerialize() /** {@inheritdoc} */ public function getVersion() { - return JsonRpcBundle::VERSION; + return BankiruJsonRpcServerBundle::VERSION; } /** {@inheritdoc} */ diff --git a/Test/DependencyInjection/JsonRpcTestExtension.php b/Test/DependencyInjection/JsonRpcTestExtension.php index 590ff18..bab59be 100644 --- a/Test/DependencyInjection/JsonRpcTestExtension.php +++ b/Test/DependencyInjection/JsonRpcTestExtension.php @@ -32,14 +32,14 @@ public function prepend(ContainerBuilder $container) 'path' => '/test/', 'resources' => '@JsonRpcTestBundle/Resources/config/jsonrpc_routes.yml', 'defaults' => [ - '_controller' => 'JsonRpcBundle:JsonRpc:jsonRpc', + '_controller' => 'BankiruJsonRpcServerBundle:JsonRpc:jsonRpc', '_format' => 'json', ], ], 'test_private' => [ 'path' => '/test/private/', 'defaults' => [ - '_controller' => 'JsonRpcBundle:JsonRpc:jsonRpc', + '_controller' => 'BankiruJsonRpcServerBundle:JsonRpc:jsonRpc', '_format' => 'json', ], 'resources' => [ diff --git a/Test/JsonRpc/AnnotationController.php b/Test/JsonRpc/AnnotationController.php index 7580c1e..4874c2a 100644 --- a/Test/JsonRpc/AnnotationController.php +++ b/Test/JsonRpc/AnnotationController.php @@ -11,7 +11,7 @@ * @package Bankiru\Api\JsonRpc\Test\JsonRpc * @Method("annotation") */ -class AnnotationController extends Controller +final class AnnotationController extends Controller { /** * @return array diff --git a/Test/JsonRpc/FailingController.php b/Test/JsonRpc/FailingController.php index d14d518..9d0a132 100644 --- a/Test/JsonRpc/FailingController.php +++ b/Test/JsonRpc/FailingController.php @@ -2,7 +2,7 @@ namespace Bankiru\Api\JsonRpc\Test\JsonRpc; -class FailingController +final class FailingController { public function failureAction() { diff --git a/Test/JsonRpc/SecurityController.php b/Test/JsonRpc/SecurityController.php new file mode 100644 index 0000000..990441a --- /dev/null +++ b/Test/JsonRpc/SecurityController.php @@ -0,0 +1,30 @@ + true]; + } + + /** + * @Method("private") + * @Security("is_granted('IS_AUTHENTICATED_FULLY')") + */ + public function privateAction() + { + return ['success' => true]; + } +} diff --git a/Test/JsonRpc/TestController.php b/Test/JsonRpc/TestController.php index a41c980..2405f2e 100644 --- a/Test/JsonRpc/TestController.php +++ b/Test/JsonRpc/TestController.php @@ -6,7 +6,7 @@ use Bankiru\Api\JsonRpc\Test\Entity\SampleEntity; use ScayTrase\Api\JsonRpc\JsonRpcRequestInterface; -class TestController +final class TestController { public function sampleAction(JsonRpcRequestInterface $request) { diff --git a/Test/Tests/AnnotatedControllerTest.php b/Test/Tests/AnnotatedControllerTest.php index 74691c5..6fc05eb 100644 --- a/Test/Tests/AnnotatedControllerTest.php +++ b/Test/Tests/AnnotatedControllerTest.php @@ -2,10 +2,8 @@ namespace Bankiru\Api\JsonRpc\Test\Tests; -use Bankiru\Api\JsonRpc\JsonRpcBundle; +use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; use ScayTrase\Api\JsonRpc\SyncResponse; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; class AnnotatedControllerTest extends JsonRpcTestCase { @@ -16,7 +14,7 @@ public function testNestedContext() $client, '/test/private/', [ - 'jsonrpc' => JsonRpcBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, 'method' => 'prefix/annotation/sub', 'id' => 'test', 'params' => [ @@ -39,7 +37,7 @@ public function testNestedContext() */ public function testEmptyRequest() { - $client = self::createClient(); + $client = self::createClient(); $this->sendRequest( $client, '/test/private/', diff --git a/Test/Tests/EntityConversionTest.php b/Test/Tests/EntityConversionTest.php index 9174500..0822bb1 100644 --- a/Test/Tests/EntityConversionTest.php +++ b/Test/Tests/EntityConversionTest.php @@ -2,7 +2,7 @@ namespace Bankiru\Api\JsonRpc\Test\Tests; -use Bankiru\Api\JsonRpc\JsonRpcBundle; +use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; use ScayTrase\Api\JsonRpc\SyncResponse; class EntityConversionTest extends JsonRpcTestCase @@ -13,7 +13,7 @@ public function testEntitySerialization() self::createClient(), '/test/', [ - 'jsonrpc' => JsonRpcBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, 'method' => 'entity', 'id' => 'test', 'params' => [ @@ -40,7 +40,7 @@ public function testPrivateEntityFields() self::createClient(), '/test/private/', [ - 'jsonrpc' => JsonRpcBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, 'method' => 'entity/private', 'id' => 'test', 'params' => [ @@ -65,14 +65,13 @@ public function testPrivateEntityFields() self::assertEquals('secret-payload', $jsonResponse->getBody()->{'private_payload'}); } - public function testNestedContext() { $response = $this->sendRequest( self::createClient(), '/test/private/', [ - 'jsonrpc' => JsonRpcBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, 'method' => 'entity/nested', 'id' => 'test', 'params' => [ diff --git a/Test/Tests/ExceptionHandlingTest.php b/Test/Tests/ExceptionHandlingTest.php index a789512..4177c20 100644 --- a/Test/Tests/ExceptionHandlingTest.php +++ b/Test/Tests/ExceptionHandlingTest.php @@ -2,7 +2,7 @@ namespace Bankiru\Api\JsonRpc\Test\Tests; -use Bankiru\Api\JsonRpc\JsonRpcBundle; +use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; use ScayTrase\Api\JsonRpc\JsonRpcError; use ScayTrase\Api\JsonRpc\JsonRpcResponseInterface; use ScayTrase\Api\JsonRpc\SyncResponse; @@ -18,7 +18,7 @@ public function testBatchWithFailingMethod() '/test/', [ [ - 'jsonrpc' => JsonRpcBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, 'method' => 'sample', 'id' => 'test', 'params' => [ @@ -27,7 +27,7 @@ public function testBatchWithFailingMethod() ], ], [ - 'jsonrpc' => JsonRpcBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, 'method' => 'exception', 'id' => 'test2', 'params' => [ @@ -37,7 +37,7 @@ public function testBatchWithFailingMethod() ], [ //notification - no id - 'jsonrpc' => JsonRpcBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, 'method' => 'notification', 'param' => [ 'notification' => 'message', @@ -68,7 +68,7 @@ public function getInvalidRequests() return [ 'No method' => [ [ - 'jsonrpc' => JsonRpcBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, 'id' => 'test', 'params' => [], ], diff --git a/Test/Tests/Fixtures/Kernel.php b/Test/Tests/Fixtures/Kernel.php index 53a93f6..59c72e8 100644 --- a/Test/Tests/Fixtures/Kernel.php +++ b/Test/Tests/Fixtures/Kernel.php @@ -2,13 +2,13 @@ namespace Bankiru\Api\JsonRpc\Test\Tests\Fixtures; -use Bankiru\Api\JsonRpc\JsonRpcBundle; +use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; use Bankiru\Api\JsonRpc\Test\JsonRpcTestBundle; -use Bankiru\Api\Rpc\RpcBundle; +use Bankiru\Api\Rpc\BankiruRpcServerBundle; use JMS\SerializerBundle\JMSSerializerBundle; -use Nelmio\ApiDocBundle\NelmioApiDocBundle; +use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; -use Symfony\Bundle\TwigBundle\TwigBundle; +use Symfony\Bundle\SecurityBundle\SecurityBundle; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest; @@ -18,21 +18,23 @@ public function registerBundles() { return [ new FrameworkBundle(), + new SecurityBundle(), + new SensioFrameworkExtraBundle(), new JMSSerializerBundle(), - new RpcBundle(), - new JsonRpcBundle(), + new BankiruRpcServerBundle(), + new BankiruJsonRpcServerBundle(), new JsonRpcTestBundle(), ]; } public function getCacheDir() { - return sys_get_temp_dir() . '/jsonrpc-test/cache'; + return __DIR__ . '/../../../build/cache/'; } public function getLogDir() { - return sys_get_temp_dir() . '/jsonrpc-test/log'; + return __DIR__ . '/../../../build/log/'; } public function registerContainerConfiguration(LoaderInterface $loader) diff --git a/Test/Tests/Fixtures/config.yml b/Test/Tests/Fixtures/config.yml index fe38e1e..7693624 100644 --- a/Test/Tests/Fixtures/config.yml +++ b/Test/Tests/Fixtures/config.yml @@ -1,12 +1,21 @@ framework: test: ~ secret: test - assets: false router: resource: "%kernel.root_dir%/routing.yml" - templating: false - translator: ~ services: logger: class: Psr\Log\NullLogger + +security: + providers: + in_memory: + memory: ~ + + firewalls: + main: + anonymous: ~ + + access_control: + - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY } diff --git a/Test/Tests/JsonRpcTestCase.php b/Test/Tests/JsonRpcTestCase.php index db78b78..0fd7dac 100644 --- a/Test/Tests/JsonRpcTestCase.php +++ b/Test/Tests/JsonRpcTestCase.php @@ -2,7 +2,11 @@ namespace Bankiru\Api\JsonRpc\Test\Tests; +use Bankiru\Api\BrowserKit\JsonRpcClient; use Bankiru\Api\JsonRpc\Test\Tests\Fixtures\Kernel; +use ScayTrase\Api\IdGenerator\UuidGenerator; +use ScayTrase\Api\JsonRpc\JsonRpcResponseInterface; +use ScayTrase\Api\Rpc\RpcRequestInterface; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\HttpKernel\Client; @@ -30,4 +34,22 @@ protected function sendRequest(Client $client = null, $endpoint, $requests) return $client->getResponse(); } + + /** + * @param RpcRequestInterface $request + * @param string $endpoint + * @param Client|null $client + * + * @return JsonRpcResponseInterface + */ + protected function sendJsonRpcRequest(RpcRequestInterface $request, $endpoint, Client $client = null) + { + if (null === $client) { + $client = static::createClient(); + } + + $jsonRpcClient = new JsonRpcClient($client, $endpoint, new UuidGenerator()); + + return $jsonRpcClient->invoke($request)->getResponse($request); + } } diff --git a/Test/Tests/RouterTest.php b/Test/Tests/RouterTest.php index f511a86..aa57565 100644 --- a/Test/Tests/RouterTest.php +++ b/Test/Tests/RouterTest.php @@ -16,7 +16,7 @@ public function testHttpRoutes() $context = new RequestContext(); $context->setMethod('POST'); $router->setContext($context); - $router->match('/test/'); + self::assertNotEmpty($router->match('/test/')); } public function testRpcRouterCollection() @@ -27,7 +27,7 @@ public function testRpcRouterCollection() /** @var Router $router */ $router = $client->getContainer()->get('rpc.endpoint_router.' . $endpoint); self::assertNotNull($router); - $collection = $router->getCollection(); + $collection = $router->getMethodCollection(); self::assertNotNull($router); self::assertInstanceOf(MethodCollection::class, $collection); diff --git a/Test/Tests/SampleControllerTest.php b/Test/Tests/SampleControllerTest.php index 54ca6e1..edbec92 100644 --- a/Test/Tests/SampleControllerTest.php +++ b/Test/Tests/SampleControllerTest.php @@ -2,7 +2,7 @@ namespace Bankiru\Api\JsonRpc\Test\Tests; -use Bankiru\Api\JsonRpc\JsonRpcBundle; +use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; use ScayTrase\Api\JsonRpc\SyncResponse; class SampleControllerTest extends JsonRpcTestCase @@ -14,19 +14,19 @@ public function testBatchRequest() '/test/', [ [ - 'jsonrpc' => JsonRpcBundle::VERSION, - 'method' => 'sample', - 'id' => 'test', - 'params' => [ + 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'method' => 'sample', + 'id' => 'test', + 'params' => [ 'param1' => 'value1', 'param2' => 100500, ], ], [ //notification - no id - 'jsonrpc' => JsonRpcBundle::VERSION, - 'method' => 'notification', - 'param' => [ + 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'method' => 'notification', + 'param' => [ 'notification' => 'message', ], ], @@ -42,10 +42,10 @@ public function testArrayRequest() self::createClient(), '/test/', [ - 'jsonrpc' => JsonRpcBundle::VERSION, - 'method' => 'array', - 'id' => 'test', - 'params' => [ + 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'method' => 'array', + 'id' => 'test', + 'params' => [ 'payload' => 'my-payload', ], ] diff --git a/Test/Tests/SecurityTest.php b/Test/Tests/SecurityTest.php new file mode 100644 index 0000000..da9fbf5 --- /dev/null +++ b/Test/Tests/SecurityTest.php @@ -0,0 +1,25 @@ +sendJsonRpcRequest(new Request('prefix/security/private', []), '/test/'); + + self::assertFalse($response->isSuccessful()); + self::assertEquals($response->getError()->getCode(), JsonRpcError::METHOD_NOT_FOUND); + } + + public function testPublicMethodRequest() + { + $response = $this->sendJsonRpcRequest(new Request('prefix/security/public', []), '/test/'); + + self::assertTrue($response->isSuccessful()); + } +} diff --git a/composer.json b/composer.json index 01425d9..437a6a8 100644 --- a/composer.json +++ b/composer.json @@ -12,17 +12,21 @@ ], "require": { "php": "~5.5 || ~7.0", - "symfony/http-kernel": "~2.7 || ~3.0", + "symfony/http-kernel": "~2.7 || ~3.0 || ~4.0", "scaytrase/json-rpc-client": "~1.0", - "bankiru/rpc-server-bundle": "~1.1", - "jms/serializer-bundle": "~1.1" + "bankiru/rpc-server-bundle": "dev-release/2.0" }, "require-dev": { - "phpunit/phpunit": "~4.8 || ~5.1", - "symfony/browser-kit": "~2.7 || ~3.0", - "symfony/translation": "~2.7 || ~3.0", + "phpunit/phpunit": "^4.8.35 || ^5.4 || ^6.0", + "sensio/framework-extra-bundle": "~3.0", + "symfony/expression-language": "~2.7 || ~3.0 || ~4.0", + "symfony/security-bundle": "~2.7 || ~3.0 || ~4.0", + "symfony/browser-kit": "~2.7 || ~3.0 || ~4.0", + "symfony/translation": "~2.7 || ~3.0 || ~4.0", "doctrine/doctrine-bundle": "~1.6", - "doctrine/orm": "~2.3" + "doctrine/orm": "~2.3", + "jms/serializer-bundle": "~1.1", + "bankiru/jsonrpc-browser-kit-client": "dev-master" }, "suggest": { "doctrine/doctrine-bundle": "For relations handler", From a2d61caa8ac6d3b69264e11fc40ff370b09e32d4 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Wed, 28 Jun 2017 12:53:59 +0300 Subject: [PATCH 02/29] Move all JMS code to Adapter namespace --- .../JMS}/Compiler/JmsDriverPass.php | 7 ++++- Adapters/JMS/HandledType.php | 27 ------------------- Adapters/JMS/HandledTypeDriver.php | 6 ++--- Adapters/JMS/Relation.php | 10 ++++++- BankiruJsonRpcServerBundle.php | 11 +------- .../BankiruJsonRpcServerExtension.php | 8 ++++++ JsonRpcBundle.php | 11 -------- Resources/config/jms.yml | 8 ++++++ Resources/config/jsonrpc.yml | 13 +++------ Serializer/HandledType.php | 12 --------- 10 files changed, 39 insertions(+), 74 deletions(-) rename {DependencyInjection => Adapters/JMS}/Compiler/JmsDriverPass.php (93%) delete mode 100644 Adapters/JMS/HandledType.php delete mode 100644 JsonRpcBundle.php create mode 100644 Resources/config/jms.yml delete mode 100644 Serializer/HandledType.php diff --git a/DependencyInjection/Compiler/JmsDriverPass.php b/Adapters/JMS/Compiler/JmsDriverPass.php similarity index 93% rename from DependencyInjection/Compiler/JmsDriverPass.php rename to Adapters/JMS/Compiler/JmsDriverPass.php index 1e0142c..c605f51 100644 --- a/DependencyInjection/Compiler/JmsDriverPass.php +++ b/Adapters/JMS/Compiler/JmsDriverPass.php @@ -1,8 +1,9 @@ has('jms_serializer')) { + return; + } + $container->register('jms_serializer.driver.relation', HandledTypeDriver::class) ->setArguments( [ diff --git a/Adapters/JMS/HandledType.php b/Adapters/JMS/HandledType.php deleted file mode 100644 index 24d7b74..0000000 --- a/Adapters/JMS/HandledType.php +++ /dev/null @@ -1,27 +0,0 @@ -handler = $handler; } - - /** - * @return string - */ - public function getHandler() - { - return $this->handler; - } -} diff --git a/Adapters/JMS/HandledTypeDriver.php b/Adapters/JMS/HandledTypeDriver.php index 04dff66..06df1e8 100644 --- a/Adapters/JMS/HandledTypeDriver.php +++ b/Adapters/JMS/HandledTypeDriver.php @@ -41,8 +41,8 @@ public function loadMetadataForClass(\ReflectionClass $class) } /** @var PropertyMetadata $propertyMetadata */ - /** @var HandledType $annot */ - $annot = $this->reader->getPropertyAnnotation($propertyMetadata->reflection, HandledType::class); + /** @var Relation $annot */ + $annot = $this->reader->getPropertyAnnotation($propertyMetadata->reflection, Relation::class); if (!$annot) { continue; } @@ -56,7 +56,7 @@ public function loadMetadataForClass(\ReflectionClass $class) $type = $propertyMetadata->type['params'][0]['name']; } - $handler = $annot->handler ?: 'Relation'; + $handler = $annot->getHandler() ?: 'Relation'; $newType = sprintf('%s<%s>', $handler, $type); if ($isCollection) { diff --git a/Adapters/JMS/Relation.php b/Adapters/JMS/Relation.php index 81f69d1..127cb95 100644 --- a/Adapters/JMS/Relation.php +++ b/Adapters/JMS/Relation.php @@ -2,6 +2,14 @@ namespace Bankiru\Api\JsonRpc\Adapters\JMS; -final class Relation extends HandledType +/** + * @Annotation + * @Target("PROPERTY") + */ +class Relation { + public function getHandler() + { + return 'Relation'; + } } diff --git a/BankiruJsonRpcServerBundle.php b/BankiruJsonRpcServerBundle.php index d078b3a..6a85a7b 100644 --- a/BankiruJsonRpcServerBundle.php +++ b/BankiruJsonRpcServerBundle.php @@ -3,21 +3,12 @@ namespace Bankiru\Api\JsonRpc; use Bankiru\Api\JsonRpc\DependencyInjection\BankiruJsonRpcServerExtension; -use Bankiru\Api\JsonRpc\DependencyInjection\Compiler\JmsDriverPass; -use Symfony\Component\DependencyInjection\Compiler\PassConfig; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; -class BankiruJsonRpcServerBundle extends Bundle +final class BankiruJsonRpcServerBundle extends Bundle { const VERSION = '2.0'; - public function build(ContainerBuilder $container) - { - parent::build($container); - $container->addCompilerPass(new JmsDriverPass(), PassConfig::TYPE_BEFORE_REMOVING); - } - public function getContainerExtension() { return new BankiruJsonRpcServerExtension(); diff --git a/DependencyInjection/BankiruJsonRpcServerExtension.php b/DependencyInjection/BankiruJsonRpcServerExtension.php index 8941e8c..b210394 100644 --- a/DependencyInjection/BankiruJsonRpcServerExtension.php +++ b/DependencyInjection/BankiruJsonRpcServerExtension.php @@ -2,8 +2,11 @@ namespace Bankiru\Api\JsonRpc\DependencyInjection; +use Bankiru\Api\JsonRpc\Adapters\JMS\Compiler\JmsDriverPass; +use JMS\SerializerBundle\JMSSerializerBundle; use Symfony\Bundle\SecurityBundle\SecurityBundle; use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; @@ -21,5 +24,10 @@ public function load(array $configs, ContainerBuilder $container) if (in_array(SecurityBundle::class, $bundles, true)) { $loader->load('security.yml'); } + + if (in_array(JMSSerializerBundle::class, $bundles, true)) { + $loader->load('jms.yml'); + $container->addCompilerPass(new JmsDriverPass(), PassConfig::TYPE_BEFORE_REMOVING); + } } } diff --git a/JsonRpcBundle.php b/JsonRpcBundle.php deleted file mode 100644 index 9bcd7b0..0000000 --- a/JsonRpcBundle.php +++ /dev/null @@ -1,11 +0,0 @@ - Date: Tue, 18 Jul 2017 08:53:08 +0300 Subject: [PATCH 03/29] Simplify adapters --- .travis.yml | 4 +- Adapters/Builtin/JsonEncodeViewListener.php | 34 +++++++++ Adapters/JMS/Compiler/JmsDriverPass.php | 73 ------------------- .../JMS/Compiler/RelationHandlerHelper.php | 56 ++++++++++++++ Adapters/JMS/HandledTypeDriver.php | 71 ------------------ Adapters/JMS/Relation.php | 15 ---- .../JMS/SerializedJsonRpcResponseListener.php | 15 ++-- BankiruJsonRpcServerBundle.php | 2 +- .../BankiruJsonRpcServerExtension.php | 69 ++++++++++++++++-- DependencyInjection/Configuration.php | 45 ++++++++++++ Http/JsonRpcHttpResponse.php | 2 +- Resources/config/adapters/builtin.yml | 4 + Resources/config/adapters/jms.yml | 6 ++ Resources/config/jms.yml | 8 -- Specification/JsonRpcRequest.php | 6 +- Specification/JsonRpcResponse.php | 4 +- Specification/RichJsonRpcRequest.php | 2 +- .../JsonRpcTestExtension.php | 66 +++++++++-------- Test/Entity/SampleEntity.php | 11 ++- Test/JsonRpc/AnnotationController.php | 3 - Test/Tests/AnnotatedControllerTest.php | 4 +- Test/Tests/EntityConversionTest.php | 41 ++++++----- Test/Tests/ExceptionHandlingTest.php | 8 +- Test/Tests/Fixtures/Kernel.php | 14 +++- Test/Tests/Fixtures/config.yml | 6 ++ Test/Tests/SampleControllerTest.php | 6 +- Test/Tests/SecurityTest.php | 2 +- dump_reference.php | 12 +++ 28 files changed, 333 insertions(+), 256 deletions(-) create mode 100644 Adapters/Builtin/JsonEncodeViewListener.php delete mode 100644 Adapters/JMS/Compiler/JmsDriverPass.php create mode 100644 Adapters/JMS/Compiler/RelationHandlerHelper.php delete mode 100644 Adapters/JMS/HandledTypeDriver.php delete mode 100644 Adapters/JMS/Relation.php create mode 100644 DependencyInjection/Configuration.php create mode 100644 Resources/config/adapters/builtin.yml create mode 100644 Resources/config/adapters/jms.yml delete mode 100644 Resources/config/jms.yml create mode 100644 dump_reference.php diff --git a/.travis.yml b/.travis.yml index 7ee0c9e..7ba3407 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,10 +33,12 @@ before_install: install: - composer require --no-update ${PACKAGES} - - if [ "$deps" = "no"] || [ -z "$deps" ]; then composer --prefer-source install; fi; + - if [ "$deps" = "no" ] || [ -z "$deps" ]; then composer --prefer-source install; fi; - if [ "$deps" = "low" ]; then composer --prefer-source --prefer-lowest --prefer-stable update; composer --prefer-source install; fi; script: - rm -rf build - mkdir -p build - vendor/bin/phpunit --colors -c phpunit.xml + - JMS_BUNDLE=1 vendor/bin/phpunit --colors -c phpunit.xml + - JMS_BUNDLE=1 DOCTRINE_BUNDLE=1 vendor/bin/phpunit --colors -c phpunit.xml diff --git a/Adapters/Builtin/JsonEncodeViewListener.php b/Adapters/Builtin/JsonEncodeViewListener.php new file mode 100644 index 0000000..7f243c9 --- /dev/null +++ b/Adapters/Builtin/JsonEncodeViewListener.php @@ -0,0 +1,34 @@ +getRequest(); + $response = $event->getResponse(); + + // No need to perform JSON-RPC serialization here + if (!$request instanceof JsonRpcRequestInterface || $request->isNotification()) { + return; + } + + // Response is already properly formatted + if ($response instanceof JsonRpcResponseInterface) { + return; + } + + $event->setResponse( + new JsonRpcResponse( + $request->getId(), + $event->getResponse() + ) + ); + } +} diff --git a/Adapters/JMS/Compiler/JmsDriverPass.php b/Adapters/JMS/Compiler/JmsDriverPass.php deleted file mode 100644 index c605f51..0000000 --- a/Adapters/JMS/Compiler/JmsDriverPass.php +++ /dev/null @@ -1,73 +0,0 @@ -has('doctrine')) { - return; - } - - if (!$container->has('jms_serializer')) { - return; - } - - $container->register('jms_serializer.driver.relation', HandledTypeDriver::class) - ->setArguments( - [ - new Reference('jms_serializer.metadata.doctrine_type_driver'), - new Reference('annotation_reader'), - ] - ); - - $container->setAlias('jms_serializer.metadata_driver', 'jms_serializer.driver.relation'); - - $container->register('jms_serializer.handler.relation', RelationsHandler::class) - ->setArguments([new Reference('doctrine.orm.entity_manager')]) - ->addTag( - 'jms_serializer.handler', - [ - 'type' => 'Relation', - 'direction' => 'serialization', - 'format' => 'json', - 'method' => 'serializeRelation', - ] - ) - ->addTag( - 'jms_serializer.handler', - [ - 'type' => 'Relation', - 'direction' => 'deserialization', - 'format' => 'json', - 'method' => 'deserializeRelation', - ] - ) - ->addTag( - 'jms_serializer.handler', - [ - 'type' => 'Relation', - 'direction' => 'serialization', - 'format' => 'json', - 'method' => 'serializeRelation', - ] - ) - ->addTag( - 'jms_serializer.handler', - [ - 'type' => 'Relation', - 'direction' => 'deserialization', - 'format' => 'json', - 'method' => 'deserializeRelation', - ] - ); - } -} diff --git a/Adapters/JMS/Compiler/RelationHandlerHelper.php b/Adapters/JMS/Compiler/RelationHandlerHelper.php new file mode 100644 index 0000000..c4f18cc --- /dev/null +++ b/Adapters/JMS/Compiler/RelationHandlerHelper.php @@ -0,0 +1,56 @@ +has($emid)) { + return; + } + + $builder->register('jms_serializer.handler.relation', RelationsHandler::class) + ->setArguments([new Reference($emid)]) + ->addTag( + 'jms_serializer.handler', + [ + 'type' => $handler, + 'direction' => 'serialization', + 'format' => 'json', + 'method' => 'serializeRelation', + ] + ) + ->addTag( + 'jms_serializer.handler', + [ + 'type' => $handler, + 'direction' => 'deserialization', + 'format' => 'json', + 'method' => 'deserializeRelation', + ] + ) + ->addTag( + 'jms_serializer.handler', + [ + 'type' => $handler . '', + 'direction' => 'serialization', + 'format' => 'json', + 'method' => 'serializeRelation', + ] + ) + ->addTag( + 'jms_serializer.handler', + [ + 'type' => $handler . '', + 'direction' => 'deserialization', + 'format' => 'json', + 'method' => 'deserializeRelation', + ] + ); + } +} diff --git a/Adapters/JMS/HandledTypeDriver.php b/Adapters/JMS/HandledTypeDriver.php deleted file mode 100644 index 06df1e8..0000000 --- a/Adapters/JMS/HandledTypeDriver.php +++ /dev/null @@ -1,71 +0,0 @@ -driver = $driver; - $this->reader = $reader; - } - - /** - * @param \ReflectionClass $class - * - * @return \Metadata\ClassMetadata - */ - public function loadMetadataForClass(\ReflectionClass $class) - { - $metadata = $this->driver->loadMetadataForClass($class); - foreach ($metadata->propertyMetadata as $key => $propertyMetadata) { - $type = $propertyMetadata->type['name']; - - if (!$propertyMetadata->reflection) { - continue; - } - - /** @var PropertyMetadata $propertyMetadata */ - /** @var Relation $annot */ - $annot = $this->reader->getPropertyAnnotation($propertyMetadata->reflection, Relation::class); - if (!$annot) { - continue; - } - - $isCollection = false; - $collectionType = null; - - if (in_array($type, ['array', 'ArrayCollection'], true)) { - $isCollection = true; - $collectionType = $type; - $type = $propertyMetadata->type['params'][0]['name']; - } - - $handler = $annot->getHandler() ?: 'Relation'; - - $newType = sprintf('%s<%s>', $handler, $type); - if ($isCollection) { - $newType = sprintf('%s<%s<%s>>', $collectionType, $handler, $type); - } - - $propertyMetadata->setType($newType); - } - - return $metadata; - } -} diff --git a/Adapters/JMS/Relation.php b/Adapters/JMS/Relation.php deleted file mode 100644 index 127cb95..0000000 --- a/Adapters/JMS/Relation.php +++ /dev/null @@ -1,15 +0,0 @@ -serializer = $serializer; } + public function __construct(Serializer $serializer) + { + $this->serializer = $serializer; + } public function onPlainResponse(ViewEvent $event) { @@ -38,12 +41,10 @@ public function onPlainResponse(ViewEvent $event) $response = new JsonRpcResponse( $request->getId(), - json_decode( - $this->serializer->serialize( - $event->getResponse(), - 'json', - $this->createSerializerContext($event) - ) + $this->serializer->toArray( + $event->getResponse(), + $this->createSerializerContext($event) + ) ); $event->setResponse($response); diff --git a/BankiruJsonRpcServerBundle.php b/BankiruJsonRpcServerBundle.php index 6a85a7b..11c0a8c 100644 --- a/BankiruJsonRpcServerBundle.php +++ b/BankiruJsonRpcServerBundle.php @@ -7,7 +7,7 @@ final class BankiruJsonRpcServerBundle extends Bundle { - const VERSION = '2.0'; + const JSONRPC_VERSION = '2.0'; public function getContainerExtension() { diff --git a/DependencyInjection/BankiruJsonRpcServerExtension.php b/DependencyInjection/BankiruJsonRpcServerExtension.php index b210394..5fd9475 100644 --- a/DependencyInjection/BankiruJsonRpcServerExtension.php +++ b/DependencyInjection/BankiruJsonRpcServerExtension.php @@ -3,10 +3,11 @@ namespace Bankiru\Api\JsonRpc\DependencyInjection; use Bankiru\Api\JsonRpc\Adapters\JMS\Compiler\JmsDriverPass; +use Bankiru\Api\JsonRpc\Adapters\JMS\Compiler\RelationHandlerHelper; +use Bankiru\Api\Rpc\RpcEvents; use JMS\SerializerBundle\JMSSerializerBundle; use Symfony\Bundle\SecurityBundle\SecurityBundle; use Symfony\Component\Config\FileLocator; -use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; @@ -16,18 +17,70 @@ final class BankiruJsonRpcServerExtension extends Extension /** {@inheritdoc} */ public function load(array $configs, ContainerBuilder $container) { - $bundles = $container->getParameter('kernel.bundles'); - $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('jsonrpc.yml'); - if (in_array(SecurityBundle::class, $bundles, true)) { - $loader->load('security.yml'); + $config = $this->processConfiguration(new Configuration(), $configs); + + $this->configureSecurity($container, $config); + $this->configureBuiltinAdapter($container, $config); + $this->configureJmsAdapter($container, $config); + + if (!empty($config['view_listener'])) { + $container->getDefinition($config['view_listener']) + ->setPublic(true) + ->addTag( + 'kernel.event_listener', + [ + 'event' => RpcEvents::VIEW, + 'method' => 'onPlainResponse', + 'priority' => -254, + ] + ); + } + } + + public function getAlias() + { + return 'jsonrpc_server'; + } + + /** + * @param ContainerBuilder $container + */ + private function configureBuiltinAdapter(ContainerBuilder $container, array $config) + { + $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config/adapters')); + + $loader->load('builtin.yml'); + } + + /** + * @param ContainerBuilder $container + */ + private function configureJmsAdapter(ContainerBuilder $container, array $config) + { + if (!in_array(JMSSerializerBundle::class, $container->getParameter('kernel.bundles'), true)) { + return; } - if (in_array(JMSSerializerBundle::class, $bundles, true)) { - $loader->load('jms.yml'); - $container->addCompilerPass(new JmsDriverPass(), PassConfig::TYPE_BEFORE_REMOVING); + $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config/adapters')); + $loader->load('jms.yml'); + + + foreach ($config['adapters']['jms']['relation_handlers'] as $handler => $emid) { + RelationHandlerHelper::ConfigureRelationHandler($container, $handler, $emid); + } + } + + /** + * @param ContainerBuilder $container + */ + private function configureSecurity(ContainerBuilder $container, array $config) + { + if (in_array(SecurityBundle::class, $container->getParameter('kernel.bundles'), true)) { + $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); + $loader->load('security.yml'); } } } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php new file mode 100644 index 0000000..da8a18d --- /dev/null +++ b/DependencyInjection/Configuration.php @@ -0,0 +1,45 @@ +root('jsonrpc_server'); + + $root->children() + ->scalarNode('view_listener') + ->info('View listener service ID') + ->defaultValue('jsonrpc.builtin_adapter.view_listener'); + + $adapters = $root->children()->arrayNode('adapters'); + $this->configureJms($adapters); + + return $builder; + } + + /** + * @param ArrayNodeDefinition $adapters + */ + private function configureJms(ArrayNodeDefinition $adapters) + { + $jms = $adapters->children()->arrayNode('jms'); + + $jms->children() + ->arrayNode('relation_handlers') + ->fixXmlConfig('relation_handler') + ->useAttributeAsKey('handler') + ->scalarPrototype() + ->info( + 'Key: Relation handler name (i.e. "Relation"), Value: service ID for relation handler entity manager' + ) + ->isRequired(); + } +} diff --git a/Http/JsonRpcHttpResponse.php b/Http/JsonRpcHttpResponse.php index a5848e8..af333c8 100644 --- a/Http/JsonRpcHttpResponse.php +++ b/Http/JsonRpcHttpResponse.php @@ -41,7 +41,7 @@ public function __construct($jsonRpc = null, $status = 200, array $headers = []) private function formatJsonRpcResponse(JsonRpcResponseInterface $jsonRpc) { $data = [ - 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::JSONRPC_VERSION, 'id' => $jsonRpc->getId(), ]; diff --git a/Resources/config/adapters/builtin.yml b/Resources/config/adapters/builtin.yml new file mode 100644 index 0000000..9a1b405 --- /dev/null +++ b/Resources/config/adapters/builtin.yml @@ -0,0 +1,4 @@ +services: + jsonrpc.builtin_adapter.view_listener: + class: Bankiru\Api\JsonRpc\Adapters\Builtin\JsonEncodeViewListener + public: false diff --git a/Resources/config/adapters/jms.yml b/Resources/config/adapters/jms.yml new file mode 100644 index 0000000..d396920 --- /dev/null +++ b/Resources/config/adapters/jms.yml @@ -0,0 +1,6 @@ +services: + jsonrpc.jms_adapter.view_listener: + class: Bankiru\Api\JsonRpc\Adapters\JMS\SerializedJsonRpcResponseListener + arguments: + - "@jms_serializer" + public: false diff --git a/Resources/config/jms.yml b/Resources/config/jms.yml deleted file mode 100644 index 1e3ed9e..0000000 --- a/Resources/config/jms.yml +++ /dev/null @@ -1,8 +0,0 @@ -services: - - jsonrpc.view.serialize_response: - class: Bankiru\Api\JsonRpc\Adapters\JMS\SerializedJsonRpcResponseListener - arguments: - - "@jms_serializer" - tags: - - { name: kernel.event_listener, event: rpc.view, method: onPlainResponse, priority: -254 } diff --git a/Specification/JsonRpcRequest.php b/Specification/JsonRpcRequest.php index e886532..821862d 100644 --- a/Specification/JsonRpcRequest.php +++ b/Specification/JsonRpcRequest.php @@ -38,8 +38,8 @@ public static function fromStdClass(\stdClass $source) throw InvalidRequestException::missingFields($missing); } - if (BankiruJsonRpcServerBundle::VERSION !== $source->jsonrpc) { - throw InvalidRequestException::invalidVersion(BankiruJsonRpcServerBundle::VERSION, $source->jsonrpc); + if (BankiruJsonRpcServerBundle::JSONRPC_VERSION !== $source->jsonrpc) { + throw InvalidRequestException::invalidVersion(BankiruJsonRpcServerBundle::JSONRPC_VERSION, $source->jsonrpc); } $request->id = isset($source->id) ? $source->id : null; @@ -69,7 +69,7 @@ public function jsonSerialize() /** {@inheritdoc} */ public function getVersion() { - return BankiruJsonRpcServerBundle::VERSION; + return BankiruJsonRpcServerBundle::JSONRPC_VERSION; } /** @return string */ diff --git a/Specification/JsonRpcResponse.php b/Specification/JsonRpcResponse.php index 5cb219c..3ee27e4 100644 --- a/Specification/JsonRpcResponse.php +++ b/Specification/JsonRpcResponse.php @@ -33,14 +33,14 @@ public function __construct($id = null, $body = null, RpcErrorInterface $error = /** {@inheritdoc} */ public function getVersion() { - return BankiruJsonRpcServerBundle::VERSION; + return BankiruJsonRpcServerBundle::JSONRPC_VERSION; } /** {@inheritdoc} */ public function jsonSerialize() { $result = [ - self::VERSION_FIELD => BankiruJsonRpcServerBundle::VERSION, + self::VERSION_FIELD => BankiruJsonRpcServerBundle::JSONRPC_VERSION, self::ID_FIELD => $this->getId(), ]; diff --git a/Specification/RichJsonRpcRequest.php b/Specification/RichJsonRpcRequest.php index 1a04d4d..4dd305d 100644 --- a/Specification/RichJsonRpcRequest.php +++ b/Specification/RichJsonRpcRequest.php @@ -52,7 +52,7 @@ public function jsonSerialize() /** {@inheritdoc} */ public function getVersion() { - return BankiruJsonRpcServerBundle::VERSION; + return BankiruJsonRpcServerBundle::JSONRPC_VERSION; } /** {@inheritdoc} */ diff --git a/Test/DependencyInjection/JsonRpcTestExtension.php b/Test/DependencyInjection/JsonRpcTestExtension.php index bab59be..9cf66c3 100644 --- a/Test/DependencyInjection/JsonRpcTestExtension.php +++ b/Test/DependencyInjection/JsonRpcTestExtension.php @@ -2,6 +2,7 @@ namespace Bankiru\Api\JsonRpc\Test\DependencyInjection; +use JMS\SerializerBundle\JMSSerializerBundle; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\HttpKernel\DependencyInjection\Extension; @@ -20,39 +21,40 @@ public function load(array $configs, ContainerBuilder $container) */ public function prepend(ContainerBuilder $container) { - foreach ($container->getExtensions() as $name => $extension) { - switch ($name) { - case 'rpc': - $container->prependExtensionConfig( - $name, - [ - 'router' => [ - 'endpoints' => [ - 'test' => [ - 'path' => '/test/', - 'resources' => '@JsonRpcTestBundle/Resources/config/jsonrpc_routes.yml', - 'defaults' => [ - '_controller' => 'BankiruJsonRpcServerBundle:JsonRpc:jsonRpc', - '_format' => 'json', - ], - ], - 'test_private' => [ - 'path' => '/test/private/', - 'defaults' => [ - '_controller' => 'BankiruJsonRpcServerBundle:JsonRpc:jsonRpc', - '_format' => 'json', - ], - 'resources' => [ - '@JsonRpcTestBundle/Resources/config/jsonrpc_routes.yml', - '@JsonRpcTestBundle/Resources/config/jsonrpc_private.yml', - ], - ], - ], + $container->prependExtensionConfig( + 'rpc', + [ + 'router' => [ + 'endpoints' => [ + 'test' => [ + 'path' => '/test/', + 'resources' => '@JsonRpcTestBundle/Resources/config/jsonrpc_routes.yml', + 'defaults' => [ + '_controller' => 'BankiruJsonRpcServerBundle:JsonRpc:jsonRpc', + '_format' => 'json', ], - ] - ); - break; - } + ], + 'test_private' => [ + 'path' => '/test/private/', + 'defaults' => [ + '_controller' => 'BankiruJsonRpcServerBundle:JsonRpc:jsonRpc', + '_format' => 'json', + ], + 'resources' => [ + '@JsonRpcTestBundle/Resources/config/jsonrpc_routes.yml', + '@JsonRpcTestBundle/Resources/config/jsonrpc_private.yml', + ], + ], + ], + ], + ] + ); + + if (in_array(JMSSerializerBundle::class, $container->getParameter('kernel.bundles'), true)) { + $container->prependExtensionConfig( + 'jsonrpc_server', + ['view_listener' => 'jsonrpc.jms_adapter.view_listener'] + ); } } } diff --git a/Test/Entity/SampleEntity.php b/Test/Entity/SampleEntity.php index 412929a..5d7a227 100644 --- a/Test/Entity/SampleEntity.php +++ b/Test/Entity/SampleEntity.php @@ -8,7 +8,7 @@ use JMS\Serializer\Annotation\Since; use JMS\Serializer\Annotation\Type; -class SampleEntity +class SampleEntity implements \JsonSerializable { /** * @var int|null @@ -57,4 +57,13 @@ public function getPayload() { return $this->payload; } + + /** {@inheritdoc} */ + public function jsonSerialize() + { + return [ + 'id' => $this->id, + 'sample_payload' => $this->payload, + ]; + } } diff --git a/Test/JsonRpc/AnnotationController.php b/Test/JsonRpc/AnnotationController.php index 4874c2a..2b78c39 100644 --- a/Test/JsonRpc/AnnotationController.php +++ b/Test/JsonRpc/AnnotationController.php @@ -6,9 +6,6 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller; /** - * Class AnnotationController - * - * @package Bankiru\Api\JsonRpc\Test\JsonRpc * @Method("annotation") */ final class AnnotationController extends Controller diff --git a/Test/Tests/AnnotatedControllerTest.php b/Test/Tests/AnnotatedControllerTest.php index 6fc05eb..d1f8f90 100644 --- a/Test/Tests/AnnotatedControllerTest.php +++ b/Test/Tests/AnnotatedControllerTest.php @@ -14,7 +14,7 @@ public function testNestedContext() $client, '/test/private/', [ - 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::JSONRPC_VERSION, 'method' => 'prefix/annotation/sub', 'id' => 'test', 'params' => [ @@ -28,7 +28,7 @@ public function testNestedContext() self::assertInstanceOf(\stdClass::class, $content); $jsonResponse = new SyncResponse($content); - self::assertTrue($jsonResponse->isSuccessful()); + self::assertTrue($jsonResponse->isSuccessful(), json_encode($content, JSON_PRETTY_PRINT)); } /** diff --git a/Test/Tests/EntityConversionTest.php b/Test/Tests/EntityConversionTest.php index 0822bb1..2ccbc71 100644 --- a/Test/Tests/EntityConversionTest.php +++ b/Test/Tests/EntityConversionTest.php @@ -13,7 +13,7 @@ public function testEntitySerialization() self::createClient(), '/test/', [ - 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::JSONRPC_VERSION, 'method' => 'entity', 'id' => 'test', 'params' => [ @@ -28,10 +28,13 @@ public function testEntitySerialization() $jsonResponse = new SyncResponse($content); self::assertTrue($jsonResponse->isSuccessful(), json_encode($content, JSON_PRETTY_PRINT)); - self::assertTrue(isset($jsonResponse->getBody()->{'sample_payload'})); + self::assertTrue(isset($jsonResponse->getBody()->{'sample_payload'}), json_encode($content, JSON_PRETTY_PRINT)); self::assertEquals('my-payload', $jsonResponse->getBody()->{'sample_payload'}); - self::assertFalse(isset($jsonResponse->getBody()->{'private_payload'})); + self::assertFalse( + isset($jsonResponse->getBody()->{'private_payload'}), + json_encode($content, JSON_PRETTY_PRINT) + ); } public function testPrivateEntityFields() @@ -40,7 +43,7 @@ public function testPrivateEntityFields() self::createClient(), '/test/private/', [ - 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::JSONRPC_VERSION, 'method' => 'entity/private', 'id' => 'test', 'params' => [ @@ -54,15 +57,17 @@ public function testPrivateEntityFields() self::assertInstanceOf(\stdClass::class, $content); $jsonResponse = new SyncResponse($content); - self::assertTrue($jsonResponse->isSuccessful()); + self::assertTrue($jsonResponse->isSuccessful(), json_encode($content, JSON_PRETTY_PRINT)); self::assertTrue(isset($jsonResponse->getBody()->{'sample_payload'})); self::assertEquals('my-payload', $jsonResponse->getBody()->{'sample_payload'}); - self::assertTrue( - isset($jsonResponse->getBody()->{'private_payload'}), - json_encode($content, JSON_PRETTY_PRINT) - ); - self::assertEquals('secret-payload', $jsonResponse->getBody()->{'private_payload'}); + if (static::$kernel->getContainer()->has('jms_serializer')) { + self::assertTrue( + isset($jsonResponse->getBody()->{'private_payload'}), + json_encode($content, JSON_PRETTY_PRINT) + ); + self::assertEquals('secret-payload', $jsonResponse->getBody()->{'private_payload'}); + } } public function testNestedContext() @@ -71,7 +76,7 @@ public function testNestedContext() self::createClient(), '/test/private/', [ - 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::JSONRPC_VERSION, 'method' => 'entity/nested', 'id' => 'test', 'params' => [ @@ -85,14 +90,16 @@ public function testNestedContext() self::assertInstanceOf(\stdClass::class, $content); $jsonResponse = new SyncResponse($content); - self::assertTrue($jsonResponse->isSuccessful()); + self::assertTrue($jsonResponse->isSuccessful(), json_encode($content, JSON_PRETTY_PRINT)); self::assertTrue(isset($jsonResponse->getBody()->{'sample_payload'})); self::assertEquals('my-payload', $jsonResponse->getBody()->{'sample_payload'}); - self::assertTrue( - isset($jsonResponse->getBody()->{'private_payload'}), - json_encode($content, JSON_PRETTY_PRINT) - ); - self::assertEquals('secret-payload', $jsonResponse->getBody()->{'private_payload'}); + if (static::$kernel->getContainer()->has('jms_serializer')) { + self::assertTrue( + isset($jsonResponse->getBody()->{'private_payload'}), + json_encode($content, JSON_PRETTY_PRINT) + ); + self::assertEquals('secret-payload', $jsonResponse->getBody()->{'private_payload'}); + } } } diff --git a/Test/Tests/ExceptionHandlingTest.php b/Test/Tests/ExceptionHandlingTest.php index 4177c20..a7cf33b 100644 --- a/Test/Tests/ExceptionHandlingTest.php +++ b/Test/Tests/ExceptionHandlingTest.php @@ -18,7 +18,7 @@ public function testBatchWithFailingMethod() '/test/', [ [ - 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::JSONRPC_VERSION, 'method' => 'sample', 'id' => 'test', 'params' => [ @@ -27,7 +27,7 @@ public function testBatchWithFailingMethod() ], ], [ - 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::JSONRPC_VERSION, 'method' => 'exception', 'id' => 'test2', 'params' => [ @@ -37,7 +37,7 @@ public function testBatchWithFailingMethod() ], [ //notification - no id - 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::JSONRPC_VERSION, 'method' => 'notification', 'param' => [ 'notification' => 'message', @@ -68,7 +68,7 @@ public function getInvalidRequests() return [ 'No method' => [ [ - 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::JSONRPC_VERSION, 'id' => 'test', 'params' => [], ], diff --git a/Test/Tests/Fixtures/Kernel.php b/Test/Tests/Fixtures/Kernel.php index 59c72e8..ce9ed09 100644 --- a/Test/Tests/Fixtures/Kernel.php +++ b/Test/Tests/Fixtures/Kernel.php @@ -5,6 +5,7 @@ use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; use Bankiru\Api\JsonRpc\Test\JsonRpcTestBundle; use Bankiru\Api\Rpc\BankiruRpcServerBundle; +use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; use JMS\SerializerBundle\JMSSerializerBundle; use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; @@ -16,15 +17,24 @@ class Kernel extends KernelForTest { public function registerBundles() { - return [ + $bundles = [ new FrameworkBundle(), new SecurityBundle(), new SensioFrameworkExtraBundle(), - new JMSSerializerBundle(), new BankiruRpcServerBundle(), new BankiruJsonRpcServerBundle(), new JsonRpcTestBundle(), ]; + + if (false !== getenv('JMS_BUNDLE')) { + $bundles[] = new JMSSerializerBundle(); + } + + if (false !== getenv('DOCTRINE_BUNDLE')) { + $bundles[] = new DoctrineBundle(); + } + + return $bundles; } public function getCacheDir() diff --git a/Test/Tests/Fixtures/config.yml b/Test/Tests/Fixtures/config.yml index 7693624..b9931e3 100644 --- a/Test/Tests/Fixtures/config.yml +++ b/Test/Tests/Fixtures/config.yml @@ -19,3 +19,9 @@ security: access_control: - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY } + +jsonrpc_server: + adapters: + jms: + relation_handlers: + Relation: doctrine.orm.entity_manager diff --git a/Test/Tests/SampleControllerTest.php b/Test/Tests/SampleControllerTest.php index edbec92..1766dcf 100644 --- a/Test/Tests/SampleControllerTest.php +++ b/Test/Tests/SampleControllerTest.php @@ -14,7 +14,7 @@ public function testBatchRequest() '/test/', [ [ - 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::JSONRPC_VERSION, 'method' => 'sample', 'id' => 'test', 'params' => [ @@ -24,7 +24,7 @@ public function testBatchRequest() ], [ //notification - no id - 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::JSONRPC_VERSION, 'method' => 'notification', 'param' => [ 'notification' => 'message', @@ -42,7 +42,7 @@ public function testArrayRequest() self::createClient(), '/test/', [ - 'jsonrpc' => BankiruJsonRpcServerBundle::VERSION, + 'jsonrpc' => BankiruJsonRpcServerBundle::JSONRPC_VERSION, 'method' => 'array', 'id' => 'test', 'params' => [ diff --git a/Test/Tests/SecurityTest.php b/Test/Tests/SecurityTest.php index da9fbf5..2abb5fa 100644 --- a/Test/Tests/SecurityTest.php +++ b/Test/Tests/SecurityTest.php @@ -20,6 +20,6 @@ public function testPublicMethodRequest() { $response = $this->sendJsonRpcRequest(new Request('prefix/security/public', []), '/test/'); - self::assertTrue($response->isSuccessful()); + self::assertTrue($response->isSuccessful(), json_encode($response, JSON_PRETTY_PRINT)); } } diff --git a/dump_reference.php b/dump_reference.php new file mode 100644 index 0000000..f599364 --- /dev/null +++ b/dump_reference.php @@ -0,0 +1,12 @@ +dump(new Configuration()); + + From 3ea18296b32ca41b37e8e35e10c9986ffa0adca9 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Tue, 18 Jul 2017 09:09:43 +0300 Subject: [PATCH 04/29] Fallback to prototype('scalar') --- DependencyInjection/Configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index da8a18d..7b161d9 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -36,7 +36,7 @@ private function configureJms(ArrayNodeDefinition $adapters) ->arrayNode('relation_handlers') ->fixXmlConfig('relation_handler') ->useAttributeAsKey('handler') - ->scalarPrototype() + ->prototype('scalar') ->info( 'Key: Relation handler name (i.e. "Relation"), Value: service ID for relation handler entity manager' ) From 0ab2fb2903f47d6c469f1e16fafa70eab8a7c5df Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Tue, 18 Jul 2017 09:19:42 +0300 Subject: [PATCH 05/29] Update dev deps --- .travis.yml | 52 ++++++++++++++++------------ Specification/RichJsonRpcRequest.php | 4 +-- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7ba3407..234a3d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,40 +1,46 @@ language: php php: - - 5.5 - - 5.6 - - 7 - - 7.1 - nightly - - hhvm + - 7.1 + - 7 + - 5.6 + - 5.5 + +## Run on container environment +sudo: false + +## Cache composer bits +cache: + directories: + - $HOME/.composer/cache env: - - PACKAGES='symfony/symfony=2.7.*' deps='no' - - PACKAGES='symfony/symfony=2.8.*' deps='no' - - PACKAGES='symfony/symfony=3.2.*' deps='no' - - PACKAGES='symfony/symfony=3.3.*' deps='no' - - PACKAGES='symfony/symfony=~3.4@dev' deps='no' + - PACKAGES='symfony/symfony=~3.4@dev' + - PACKAGES='symfony/symfony=3.3.*' + - PACKAGES='symfony/symfony=3.2.*' + - PACKAGES='symfony/symfony=2.8.*' + - PACKAGES='symfony/symfony=2.7.*' matrix: - include: - - php: 5.5 - env: PACKAGES='symfony/symfony=2.7.*' deps='low' - - php: 7.1 - env: PACKAGES='symfony/symfony=~4.0@dev sensio/framework-extra-bundle=~4.0@dev' deps='no' - - php: nightly - env: PACKAGES='symfony/symfony=~4.0@dev sensio/framework-extra-bundle=~4.0@dev' deps='no' - - allow_failures: - - php: hhvm - - php: nightly + fast_finish: true + include: + - php: 7.1 + env: PACKAGES='symfony/symfony=~4.0@dev' STABILITY=dev + - php: nightly + env: PACKAGES='symfony/symfony=~4.0@dev' STABILITY=dev + + allow_failures: + - php: nightly + - env: PACKAGES='symfony/symfony=~4.0@dev' STABILITY=dev before_install: - travis_retry composer self-update install: - composer require --no-update ${PACKAGES} - - if [ "$deps" = "no" ] || [ -z "$deps" ]; then composer --prefer-source install; fi; - - if [ "$deps" = "low" ]; then composer --prefer-source --prefer-lowest --prefer-stable update; composer --prefer-source install; fi; + - if [ "$STABILITY" = "dev" ]; then composer config minimum-stability dev; fi; + - composer --prefer-source install script: - rm -rf build diff --git a/Specification/RichJsonRpcRequest.php b/Specification/RichJsonRpcRequest.php index 4dd305d..6c3e384 100644 --- a/Specification/RichJsonRpcRequest.php +++ b/Specification/RichJsonRpcRequest.php @@ -3,11 +3,11 @@ namespace Bankiru\Api\JsonRpc\Specification; use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; -use Bankiru\Api\Rpc\Http\RequestInterface; +use Bankiru\Api\Rpc\RpcRequestInterface; use ScayTrase\Api\JsonRpc\JsonRpcRequestInterface; use Symfony\Component\HttpFoundation\ParameterBag; -final class RichJsonRpcRequest implements RequestInterface, JsonRpcRequestInterface +final class RichJsonRpcRequest implements RpcRequestInterface, JsonRpcRequestInterface { /** @var JsonRpcRequestInterface */ private $request; From 29e363997b59da696829f48e8e4f1aeb098ae057 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Wed, 19 Jul 2017 09:54:03 +0300 Subject: [PATCH 06/29] Split integration and unit testing --- .gitignore | 45 -------------- .scrutinizer.yml | 2 +- .travis.yml | 7 ++- .../JsonRpcTestExtension.php | 60 ------------------- Test/Entity/SampleEntity.php | 31 ++-------- Test/JsonRpcTestBundle.php | 2 +- .../Kernel.php => Kernel/TestKernel.php} | 31 ++++++++-- Test/Kernel/config.yml | 45 ++++++++++++++ Test/Kernel/config_doctrine.yml | 0 Test/Kernel/config_jms.yml | 6 ++ Test/{Tests/Fixtures => Kernel}/routing.yml | 0 .../config/serializer/Entity.SampleEntity.yml | 14 +++++ Test/Tests/AnnotatedControllerTest.php | 2 +- Test/Tests/EntityConversionTest.php | 14 ++--- Test/Tests/ExceptionHandlingTest.php | 2 +- Test/Tests/Fixtures/config.yml | 27 --------- Test/Tests/JsonRpcTestCase.php | 4 +- Test/Tests/RouterTest.php | 2 +- Test/Tests/SampleControllerTest.php | 6 +- Test/Tests/SecurityTest.php | 1 - Tests/JsonRpcHttpResponseTest.php | 43 +++++++++++++ composer.json | 2 +- phpunit.xml | 7 ++- 23 files changed, 168 insertions(+), 185 deletions(-) delete mode 100644 Test/DependencyInjection/JsonRpcTestExtension.php rename Test/{Tests/Fixtures/Kernel.php => Kernel/TestKernel.php} (65%) create mode 100644 Test/Kernel/config.yml create mode 100644 Test/Kernel/config_doctrine.yml create mode 100644 Test/Kernel/config_jms.yml rename Test/{Tests/Fixtures => Kernel}/routing.yml (100%) create mode 100644 Test/Resources/config/serializer/Entity.SampleEntity.yml delete mode 100644 Test/Tests/Fixtures/config.yml create mode 100644 Tests/JsonRpcHttpResponseTest.php diff --git a/.gitignore b/.gitignore index 1d5c172..389e076 100644 --- a/.gitignore +++ b/.gitignore @@ -1,55 +1,10 @@ -/*.pnproj /.idea -.htaccess -atlassian-ide-plugin.xml - -### Symfony template -# Cache and logs (Symfony2) -/app/cache/* -/app/logs/* -!app/cache/.gitkeep -!app/logs/.gitkeep - -# Cache and logs (Symfony3) -/var/* -!/var/cache -/var/cache/* -!var/cache/.gitkeep -!/var/logs -/var/logs/* -!var/logs/.gitkeep -!/var/sessions -/var/sessions/* -!var/sessions/.gitkeep -var/SymfonyRequirements.php - -# Parameters -/app/config/parameters.yml -/app/config/parameters.local.yml -/app/config/parameters.ini - -# Managed by Composer -/app/bootstrap.php.cache -/var/bootstrap.php.cache -/bin/* -!bin/console -bin/symfony_requirements /vendor/ -# Assets and user uploads -/web/bundles/ -/web/uploads/ - -# PHPUnit -/app/phpunit.xml - # Build data /build/ /target/ # Any PHARs *.phar - -Thumbs.db - composer.lock diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 7b7e67b..d0b662d 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -2,7 +2,7 @@ build: tests: override: - - command: phpunit --coverage-clover=build/clover.xml + command: JMS_BUNDLE=1 DOCTRINE_BUNDLE=1 phpunit --coverage-clover=build/clover.xml coverage: file: build/clover.xml format: php-clover diff --git a/.travis.yml b/.travis.yml index 234a3d6..0704a17 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,6 +45,7 @@ install: script: - rm -rf build - mkdir -p build - - vendor/bin/phpunit --colors -c phpunit.xml - - JMS_BUNDLE=1 vendor/bin/phpunit --colors -c phpunit.xml - - JMS_BUNDLE=1 DOCTRINE_BUNDLE=1 vendor/bin/phpunit --colors -c phpunit.xml + - vendor/bin/phpunit --colors -c phpunit.xml --testsuite unit + - vendor/bin/phpunit --colors -c phpunit.xml --testsuite integration + - JMS_BUNDLE=1 vendor/bin/phpunit --colors -c phpunit.xml --testsuite integration + - JMS_BUNDLE=1 DOCTRINE_BUNDLE=1 vendor/bin/phpunit --colors -c phpunit.xml --testsuite integration diff --git a/Test/DependencyInjection/JsonRpcTestExtension.php b/Test/DependencyInjection/JsonRpcTestExtension.php deleted file mode 100644 index 9cf66c3..0000000 --- a/Test/DependencyInjection/JsonRpcTestExtension.php +++ /dev/null @@ -1,60 +0,0 @@ -prependExtensionConfig( - 'rpc', - [ - 'router' => [ - 'endpoints' => [ - 'test' => [ - 'path' => '/test/', - 'resources' => '@JsonRpcTestBundle/Resources/config/jsonrpc_routes.yml', - 'defaults' => [ - '_controller' => 'BankiruJsonRpcServerBundle:JsonRpc:jsonRpc', - '_format' => 'json', - ], - ], - 'test_private' => [ - 'path' => '/test/private/', - 'defaults' => [ - '_controller' => 'BankiruJsonRpcServerBundle:JsonRpc:jsonRpc', - '_format' => 'json', - ], - 'resources' => [ - '@JsonRpcTestBundle/Resources/config/jsonrpc_routes.yml', - '@JsonRpcTestBundle/Resources/config/jsonrpc_private.yml', - ], - ], - ], - ], - ] - ); - - if (in_array(JMSSerializerBundle::class, $container->getParameter('kernel.bundles'), true)) { - $container->prependExtensionConfig( - 'jsonrpc_server', - ['view_listener' => 'jsonrpc.jms_adapter.view_listener'] - ); - } - } -} diff --git a/Test/Entity/SampleEntity.php b/Test/Entity/SampleEntity.php index 5d7a227..ce679db 100644 --- a/Test/Entity/SampleEntity.php +++ b/Test/Entity/SampleEntity.php @@ -2,33 +2,14 @@ namespace Bankiru\Api\JsonRpc\Test\Entity; -use JMS\Serializer\Annotation\Expose; -use JMS\Serializer\Annotation\Groups; -use JMS\Serializer\Annotation\SerializedName; -use JMS\Serializer\Annotation\Since; -use JMS\Serializer\Annotation\Type; - class SampleEntity implements \JsonSerializable { - /** - * @var int|null - */ + /** @var int|null */ private $id; - /** - * @var string - * @Type("string") - * @SerializedName("sample_payload") - * @Expose() - */ + /** @var string */ private $payload; - /** - * @var string - * @Type("string") - * @SerializedName("private_payload") - * @Expose() - * @Groups({"private"}) - * @Since("0.1") - */ + + /** @var string */ private $secret; /** @@ -62,8 +43,8 @@ public function getPayload() public function jsonSerialize() { return [ - 'id' => $this->id, - 'sample_payload' => $this->payload, + 'id' => $this->id, + 'sample_payload' => $this->payload, ]; } } diff --git a/Test/JsonRpcTestBundle.php b/Test/JsonRpcTestBundle.php index 912ea2c..bee2c84 100644 --- a/Test/JsonRpcTestBundle.php +++ b/Test/JsonRpcTestBundle.php @@ -4,6 +4,6 @@ use Symfony\Component\HttpKernel\Bundle\Bundle; -class JsonRpcTestBundle extends Bundle +final class JsonRpcTestBundle extends Bundle { } diff --git a/Test/Tests/Fixtures/Kernel.php b/Test/Kernel/TestKernel.php similarity index 65% rename from Test/Tests/Fixtures/Kernel.php rename to Test/Kernel/TestKernel.php index ce9ed09..7661a03 100644 --- a/Test/Tests/Fixtures/Kernel.php +++ b/Test/Kernel/TestKernel.php @@ -1,6 +1,6 @@ getName() . '/cache'; } public function getLogDir() { - return __DIR__ . '/../../../build/log/'; + return __DIR__ . '/../../build/' . $this->getName() . '/log'; } public function registerContainerConfiguration(LoaderInterface $loader) { $loader->load(__DIR__ . '/config.yml'); + + if (false !== getenv('JMS_BUNDLE')) { + $loader->load(__DIR__ . '/config_jms.yml'); + } + + if (false !== getenv('DOCTRINE_BUNDLE')) { + $loader->load(__DIR__ . '/config_doctrine.yml'); + } + } + + public function getName() + { + $name = 'jsorpc_test'; + + if (false !== getenv('JMS_BUNDLE')) { + $name .= '_jms'; + } + + if (false !== getenv('DOCTRINE_BUNDLE')) { + $name .= '_doctrine'; + } + + return $name; } public function getEnvironment() diff --git a/Test/Kernel/config.yml b/Test/Kernel/config.yml new file mode 100644 index 0000000..7d3dbf1 --- /dev/null +++ b/Test/Kernel/config.yml @@ -0,0 +1,45 @@ +framework: + test: ~ + secret: test + router: + resource: "%kernel.root_dir%/routing.yml" + +services: + logger: + class: Psr\Log\NullLogger + +security: + providers: + in_memory: + memory: ~ + + firewalls: + main: + anonymous: ~ + + access_control: + - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY } + +jsonrpc_server: + adapters: + jms: + relation_handlers: + Relation: doctrine.orm.entity_manager + +rpc: + router: + endpoints: + test: + path: /test/ + resources: '@JsonRpcTestBundle/Resources/config/jsonrpc_routes.yml' + defaults: + _controller : BankiruJsonRpcServerBundle:JsonRpc:jsonRpc + _format: json + test_private: + path: /test/private/ + defaults: + _controller : BankiruJsonRpcServerBundle:JsonRpc:jsonRpc + _format: json + resources: + - '@JsonRpcTestBundle/Resources/config/jsonrpc_routes.yml' + - '@JsonRpcTestBundle/Resources/config/jsonrpc_private.yml' diff --git a/Test/Kernel/config_doctrine.yml b/Test/Kernel/config_doctrine.yml new file mode 100644 index 0000000..e69de29 diff --git a/Test/Kernel/config_jms.yml b/Test/Kernel/config_jms.yml new file mode 100644 index 0000000..9c99749 --- /dev/null +++ b/Test/Kernel/config_jms.yml @@ -0,0 +1,6 @@ +jms_serializer: + metadata: + auto_detection: true + +jsonrpc_server: + view_listener: jsonrpc.jms_adapter.view_listener diff --git a/Test/Tests/Fixtures/routing.yml b/Test/Kernel/routing.yml similarity index 100% rename from Test/Tests/Fixtures/routing.yml rename to Test/Kernel/routing.yml diff --git a/Test/Resources/config/serializer/Entity.SampleEntity.yml b/Test/Resources/config/serializer/Entity.SampleEntity.yml new file mode 100644 index 0000000..46c823d --- /dev/null +++ b/Test/Resources/config/serializer/Entity.SampleEntity.yml @@ -0,0 +1,14 @@ +Bankiru\Api\JsonRpc\Test\Entity\SampleEntity: + exclusion_policy: ALL + properties: + payload: + type: string + serialized_name: sample_payload + expose: true + + secret: + type: string + serialized_name: private_payload + groups: [private] + since_version: 0.1 + expose: true diff --git a/Test/Tests/AnnotatedControllerTest.php b/Test/Tests/AnnotatedControllerTest.php index d1f8f90..d14a614 100644 --- a/Test/Tests/AnnotatedControllerTest.php +++ b/Test/Tests/AnnotatedControllerTest.php @@ -5,7 +5,7 @@ use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; use ScayTrase\Api\JsonRpc\SyncResponse; -class AnnotatedControllerTest extends JsonRpcTestCase +final class AnnotatedControllerTest extends JsonRpcTestCase { public function testNestedContext() { diff --git a/Test/Tests/EntityConversionTest.php b/Test/Tests/EntityConversionTest.php index 2ccbc71..78f2248 100644 --- a/Test/Tests/EntityConversionTest.php +++ b/Test/Tests/EntityConversionTest.php @@ -5,7 +5,7 @@ use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; use ScayTrase\Api\JsonRpc\SyncResponse; -class EntityConversionTest extends JsonRpcTestCase +final class EntityConversionTest extends JsonRpcTestCase { public function testEntitySerialization() { @@ -58,15 +58,15 @@ public function testPrivateEntityFields() $jsonResponse = new SyncResponse($content); self::assertTrue($jsonResponse->isSuccessful(), json_encode($content, JSON_PRETTY_PRINT)); - self::assertTrue(isset($jsonResponse->getBody()->{'sample_payload'})); - self::assertEquals('my-payload', $jsonResponse->getBody()->{'sample_payload'}); + self::assertTrue(isset($jsonResponse->getBody()->{'sample_payload'}), json_encode($content, JSON_PRETTY_PRINT)); + self::assertEquals('my-payload', $jsonResponse->getBody()->{'sample_payload'}, json_encode($content, JSON_PRETTY_PRINT)); if (static::$kernel->getContainer()->has('jms_serializer')) { self::assertTrue( isset($jsonResponse->getBody()->{'private_payload'}), json_encode($content, JSON_PRETTY_PRINT) ); - self::assertEquals('secret-payload', $jsonResponse->getBody()->{'private_payload'}); + self::assertEquals('secret-payload', $jsonResponse->getBody()->{'private_payload'}, json_encode($content, JSON_PRETTY_PRINT)); } } @@ -91,15 +91,15 @@ public function testNestedContext() $jsonResponse = new SyncResponse($content); self::assertTrue($jsonResponse->isSuccessful(), json_encode($content, JSON_PRETTY_PRINT)); - self::assertTrue(isset($jsonResponse->getBody()->{'sample_payload'})); - self::assertEquals('my-payload', $jsonResponse->getBody()->{'sample_payload'}); + self::assertTrue(isset($jsonResponse->getBody()->{'sample_payload'}), json_encode($content, JSON_PRETTY_PRINT)); + self::assertEquals('my-payload', $jsonResponse->getBody()->{'sample_payload'}, json_encode($content, JSON_PRETTY_PRINT)); if (static::$kernel->getContainer()->has('jms_serializer')) { self::assertTrue( isset($jsonResponse->getBody()->{'private_payload'}), json_encode($content, JSON_PRETTY_PRINT) ); - self::assertEquals('secret-payload', $jsonResponse->getBody()->{'private_payload'}); + self::assertEquals('secret-payload', $jsonResponse->getBody()->{'private_payload'}, json_encode($content, JSON_PRETTY_PRINT)); } } } diff --git a/Test/Tests/ExceptionHandlingTest.php b/Test/Tests/ExceptionHandlingTest.php index a7cf33b..f64984e 100644 --- a/Test/Tests/ExceptionHandlingTest.php +++ b/Test/Tests/ExceptionHandlingTest.php @@ -7,7 +7,7 @@ use ScayTrase\Api\JsonRpc\JsonRpcResponseInterface; use ScayTrase\Api\JsonRpc\SyncResponse; -class ExceptionHandlingTest extends JsonRpcTestCase +final class ExceptionHandlingTest extends JsonRpcTestCase { public function testBatchWithFailingMethod() { diff --git a/Test/Tests/Fixtures/config.yml b/Test/Tests/Fixtures/config.yml deleted file mode 100644 index b9931e3..0000000 --- a/Test/Tests/Fixtures/config.yml +++ /dev/null @@ -1,27 +0,0 @@ -framework: - test: ~ - secret: test - router: - resource: "%kernel.root_dir%/routing.yml" - -services: - logger: - class: Psr\Log\NullLogger - -security: - providers: - in_memory: - memory: ~ - - firewalls: - main: - anonymous: ~ - - access_control: - - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY } - -jsonrpc_server: - adapters: - jms: - relation_handlers: - Relation: doctrine.orm.entity_manager diff --git a/Test/Tests/JsonRpcTestCase.php b/Test/Tests/JsonRpcTestCase.php index 0fd7dac..400c18e 100644 --- a/Test/Tests/JsonRpcTestCase.php +++ b/Test/Tests/JsonRpcTestCase.php @@ -3,7 +3,7 @@ namespace Bankiru\Api\JsonRpc\Test\Tests; use Bankiru\Api\BrowserKit\JsonRpcClient; -use Bankiru\Api\JsonRpc\Test\Tests\Fixtures\Kernel; +use Bankiru\Api\JsonRpc\Test\Kernel\TestKernel; use ScayTrase\Api\IdGenerator\UuidGenerator; use ScayTrase\Api\JsonRpc\JsonRpcResponseInterface; use ScayTrase\Api\Rpc\RpcRequestInterface; @@ -14,7 +14,7 @@ abstract class JsonRpcTestCase extends WebTestCase { protected static function getKernelClass() { - return Kernel::class; + return TestKernel::class; } /** diff --git a/Test/Tests/RouterTest.php b/Test/Tests/RouterTest.php index aa57565..40ab9c6 100644 --- a/Test/Tests/RouterTest.php +++ b/Test/Tests/RouterTest.php @@ -7,7 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\Routing\RequestContext; -class RouterTest extends WebTestCase +final class RouterTest extends WebTestCase { public function testHttpRoutes() { diff --git a/Test/Tests/SampleControllerTest.php b/Test/Tests/SampleControllerTest.php index 1766dcf..3b39cfb 100644 --- a/Test/Tests/SampleControllerTest.php +++ b/Test/Tests/SampleControllerTest.php @@ -5,7 +5,7 @@ use Bankiru\Api\JsonRpc\BankiruJsonRpcServerBundle; use ScayTrase\Api\JsonRpc\SyncResponse; -class SampleControllerTest extends JsonRpcTestCase +final class SampleControllerTest extends JsonRpcTestCase { public function testBatchRequest() { @@ -57,7 +57,7 @@ public function testArrayRequest() $jsonResponse = new SyncResponse($content); self::assertTrue($jsonResponse->isSuccessful(), json_encode($content)); - self::assertTrue(isset($jsonResponse->getBody()[0]->{'sample_payload'})); - self::assertEquals('my-payload', $jsonResponse->getBody()[0]->{'sample_payload'}); + self::assertTrue(isset($jsonResponse->getBody()[0]->{'sample_payload'}), json_encode($content, JSON_PRETTY_PRINT)); + self::assertEquals('my-payload', $jsonResponse->getBody()[0]->{'sample_payload'}, json_encode($content, JSON_PRETTY_PRINT)); } } diff --git a/Test/Tests/SecurityTest.php b/Test/Tests/SecurityTest.php index 2abb5fa..3d431b9 100644 --- a/Test/Tests/SecurityTest.php +++ b/Test/Tests/SecurityTest.php @@ -9,7 +9,6 @@ final class SecurityTest extends JsonRpcTestCase { public function testPrivateMethodRequest() { - $response = $this->sendJsonRpcRequest(new Request('prefix/security/private', []), '/test/'); self::assertFalse($response->isSuccessful()); diff --git a/Tests/JsonRpcHttpResponseTest.php b/Tests/JsonRpcHttpResponseTest.php new file mode 100644 index 0000000..5893f6a --- /dev/null +++ b/Tests/JsonRpcHttpResponseTest.php @@ -0,0 +1,43 @@ + true], null); + + $error = new JsonRpcResponse( + 'single', + null, + new JsonRpcError(JsonRpcError::METHOD_NOT_FOUND, 'Invalid method') + ); + + return [ + 'empty' => [null], + 'empty array' => [[]], + 'single success' => [clone $success], + 'single failure' => [clone $error], + 'mixed array' => [[clone $success, clone $error,]], + ]; + } + + /** + * @dataProvider getResponseVariants + * + * @param $responses + */ + public function testResponseProvidesCorrectHeaders($responses) + { + $response = new JsonRpcHttpResponse($responses); + self::assertTrue($response->isSuccessful()); + self::assertTrue($response->headers->has('Content-Type')); + self::assertEquals('application/json', $response->headers->get('Content-Type')); + } +} diff --git a/composer.json b/composer.json index 437a6a8..bb26f79 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ "doctrine/doctrine-bundle": "~1.6", "doctrine/orm": "~2.3", "jms/serializer-bundle": "~1.1", - "bankiru/jsonrpc-browser-kit-client": "dev-master" + "bankiru/jsonrpc-browser-kit-client": "~1.0" }, "suggest": { "doctrine/doctrine-bundle": "For relations handler", diff --git a/phpunit.xml b/phpunit.xml index 3b8fa98..102b3fd 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -9,17 +9,20 @@ > - - + ./Test/Tests/ + + ./Tests/ + + Test/ vendor/ build/ From 3fe5fca55c10eb79d4415363ad1730b7e8064569 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Wed, 19 Jul 2017 10:11:45 +0300 Subject: [PATCH 07/29] Add translator to jms kernel --- Test/Kernel/config_doctrine.yml | 7 +++++++ Test/Kernel/config_jms.yml | 3 +++ Test/Resources/config/doctrine/SampleEntity.orm.yml | 5 +++++ phpunit.xml | 1 + 4 files changed, 16 insertions(+) create mode 100644 Test/Resources/config/doctrine/SampleEntity.orm.yml diff --git a/Test/Kernel/config_doctrine.yml b/Test/Kernel/config_doctrine.yml index e69de29..4daa5a5 100644 --- a/Test/Kernel/config_doctrine.yml +++ b/Test/Kernel/config_doctrine.yml @@ -0,0 +1,7 @@ +doctrine: + dbal: + driver: pdo_sqlite + memory: true + orm: + auto_mapping: true + auto_generate_proxy_classes: true diff --git a/Test/Kernel/config_jms.yml b/Test/Kernel/config_jms.yml index 9c99749..d5984f6 100644 --- a/Test/Kernel/config_jms.yml +++ b/Test/Kernel/config_jms.yml @@ -1,3 +1,6 @@ +framework: + translator: ~ + jms_serializer: metadata: auto_detection: true diff --git a/Test/Resources/config/doctrine/SampleEntity.orm.yml b/Test/Resources/config/doctrine/SampleEntity.orm.yml new file mode 100644 index 0000000..a3a12a8 --- /dev/null +++ b/Test/Resources/config/doctrine/SampleEntity.orm.yml @@ -0,0 +1,5 @@ +Bankiru\Api\JsonRpc\Test\Entity\SampleEntity: + type: entity + fields: + payload: {type: string} + secret: {type: string} diff --git a/phpunit.xml b/phpunit.xml index 102b3fd..31ec208 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -23,6 +23,7 @@ Test/ + Tests/ vendor/ build/ From c1c02af88b5bd539b4c54d83de95613be5029d63 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Wed, 19 Jul 2017 10:17:29 +0300 Subject: [PATCH 08/29] Configure sample identifier --- Test/Resources/config/doctrine/SampleEntity.orm.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Test/Resources/config/doctrine/SampleEntity.orm.yml b/Test/Resources/config/doctrine/SampleEntity.orm.yml index a3a12a8..5a6ef94 100644 --- a/Test/Resources/config/doctrine/SampleEntity.orm.yml +++ b/Test/Resources/config/doctrine/SampleEntity.orm.yml @@ -1,5 +1,7 @@ Bankiru\Api\JsonRpc\Test\Entity\SampleEntity: type: entity + id: + id: { type: integer } fields: payload: {type: string} secret: {type: string} From 8af15624702a453dab75009c6aa63d7be0538612 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Wed, 19 Jul 2017 11:23:10 +0300 Subject: [PATCH 09/29] Add controller test --- Controller/JsonRpcController.php | 9 +- Controller/JsonRpcControllerNameParser.php | 2 +- .../BankiruJsonRpcServerExtension.php | 1 - Exception/InvalidRequestException.php | 8 + Exception/JsonRpcException.php | 1 - Listener/ExceptionHandlerListener.php | 14 +- Listener/NotificationResponseListener.php | 2 +- Specification/JsonRpcRequest.php | 5 +- Test/JsonRpcTestBundle.php | 1 + Test/Tests/EntityConversionTest.php | 24 +- Test/Tests/SampleControllerTest.php | 11 +- Tests/JsonRpcControllerTest.php | 238 ++++++++++++++++++ bootstrap.php | 4 +- 13 files changed, 300 insertions(+), 20 deletions(-) create mode 100644 Tests/JsonRpcControllerTest.php diff --git a/Controller/JsonRpcController.php b/Controller/JsonRpcController.php index 0064990..7770bed 100644 --- a/Controller/JsonRpcController.php +++ b/Controller/JsonRpcController.php @@ -2,6 +2,7 @@ namespace Bankiru\Api\JsonRpc\Controller; +use Bankiru\Api\JsonRpc\Exception\InvalidRequestException; use Bankiru\Api\JsonRpc\Exception\RpcMethodNotFoundException; use Bankiru\Api\JsonRpc\Http\JsonRpcHttpResponse; use Bankiru\Api\JsonRpc\Specification\JsonRpcRequest; @@ -23,14 +24,15 @@ final class JsonRpcController extends RpcController * * @throws BadRequestHttpException * @throws RpcMethodNotFoundException + * @throws InvalidRequestException */ public function jsonRpcAction(Request $request) { $request->attributes->set('_format', 'json'); $jsonrpc = json_decode($request->getContent()); - if (null === $jsonrpc || json_last_error() !== JSON_ERROR_NONE) { - throw new BadRequestHttpException('Not an valid JSON request'); + if ((!is_array($jsonrpc) && !is_object($jsonrpc)) || json_last_error() !== JSON_ERROR_NONE) { + throw new BadRequestHttpException('Not a valid JSON-RPC request'); } $singleRequest = false; @@ -41,6 +43,9 @@ public function jsonRpcAction(Request $request) $responses = []; foreach ($jsonrpc as $call) { + if (!$call instanceof \stdClass) { + throw InvalidRequestException::notAJsonRpc(); + } $response = $this->handle($call, $request->get('_route')); if (null !== $response) { $responses[] = $response; diff --git a/Controller/JsonRpcControllerNameParser.php b/Controller/JsonRpcControllerNameParser.php index a4cb7ed..8aa63be 100644 --- a/Controller/JsonRpcControllerNameParser.php +++ b/Controller/JsonRpcControllerNameParser.php @@ -10,6 +10,6 @@ final class JsonRpcControllerNameParser extends RpcControllerNameParser /** {@inheritdoc} */ protected function guessControllerClassName(BundleInterface $bundle, $controller) { - return $bundle->getNamespace().'\\JsonRpc\\'.$controller.'Controller'; + return $bundle->getNamespace() . '\\JsonRpc\\' . $controller . 'Controller'; } } diff --git a/DependencyInjection/BankiruJsonRpcServerExtension.php b/DependencyInjection/BankiruJsonRpcServerExtension.php index 5fd9475..a96d42c 100644 --- a/DependencyInjection/BankiruJsonRpcServerExtension.php +++ b/DependencyInjection/BankiruJsonRpcServerExtension.php @@ -67,7 +67,6 @@ private function configureJmsAdapter(ContainerBuilder $container, array $config) $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config/adapters')); $loader->load('jms.yml'); - foreach ($config['adapters']['jms']['relation_handlers'] as $handler => $emid) { RelationHandlerHelper::ConfigureRelationHandler($container, $handler, $emid); } diff --git a/Exception/InvalidRequestException.php b/Exception/InvalidRequestException.php index 3d7b08d..8262aa3 100644 --- a/Exception/InvalidRequestException.php +++ b/Exception/InvalidRequestException.php @@ -21,4 +21,12 @@ public static function invalidVersion($expected, $actual) sprintf('Invalid JSONRPC 2.0 Request. Version mismatch: %s expected, %s given', $expected, $actual) ); } + + public static function notAJsonRpc() + { + return self::create( + JsonRpcError::INVALID_REQUEST, + 'Not a JSONRPC 2.0 Request' + ); + } } diff --git a/Exception/JsonRpcException.php b/Exception/JsonRpcException.php index b27698a..d0687e7 100644 --- a/Exception/JsonRpcException.php +++ b/Exception/JsonRpcException.php @@ -18,7 +18,6 @@ public function __construct($message = '', $code = 0, Exception $previous = null parent::__construct($message, $code, $previous); } - /** * @return JsonRpcErrorInterface */ diff --git a/Listener/ExceptionHandlerListener.php b/Listener/ExceptionHandlerListener.php index 768a626..0283f00 100644 --- a/Listener/ExceptionHandlerListener.php +++ b/Listener/ExceptionHandlerListener.php @@ -13,15 +13,17 @@ final class ExceptionHandlerListener { - private $debug = false; + private $debug; /** * ExceptionHandlerListener constructor. * * @param bool $debug */ - public function __construct($debug) { $this->debug = (bool)$debug; } - + public function __construct($debug = false) + { + $this->debug = (bool)$debug; + } public function onJsonRpcException(GetExceptionResponseEvent $event) { @@ -37,13 +39,15 @@ public function onJsonRpcException(GetExceptionResponseEvent $event) JsonRpcException::create( JsonRpcError::INVALID_PARAMS, $exception->getMessage(), - $exception->getTrace()); + $exception->getTrace() + ); } elseif ($exception instanceof MethodNotFoundException) { $exception = JsonRpcException::create( JsonRpcError::METHOD_NOT_FOUND, $exception->getMessage(), - $exception->getTrace()); + $exception->getTrace() + ); } if ($exception instanceof JsonRpcExceptionInterface) { diff --git a/Listener/NotificationResponseListener.php b/Listener/NotificationResponseListener.php index a04bf60..a2e9c1b 100644 --- a/Listener/NotificationResponseListener.php +++ b/Listener/NotificationResponseListener.php @@ -17,7 +17,7 @@ final class NotificationResponseListener */ public function onNullResponse(ViewEvent $event) { - $request = $event->getRequest(); + $request = $event->getRequest(); if (!$request instanceof JsonRpcRequestInterface) { return; diff --git a/Specification/JsonRpcRequest.php b/Specification/JsonRpcRequest.php index 821862d..5ba298d 100644 --- a/Specification/JsonRpcRequest.php +++ b/Specification/JsonRpcRequest.php @@ -39,7 +39,10 @@ public static function fromStdClass(\stdClass $source) } if (BankiruJsonRpcServerBundle::JSONRPC_VERSION !== $source->jsonrpc) { - throw InvalidRequestException::invalidVersion(BankiruJsonRpcServerBundle::JSONRPC_VERSION, $source->jsonrpc); + throw InvalidRequestException::invalidVersion( + BankiruJsonRpcServerBundle::JSONRPC_VERSION, + $source->jsonrpc + ); } $request->id = isset($source->id) ? $source->id : null; diff --git a/Test/JsonRpcTestBundle.php b/Test/JsonRpcTestBundle.php index bee2c84..ccf8185 100644 --- a/Test/JsonRpcTestBundle.php +++ b/Test/JsonRpcTestBundle.php @@ -4,6 +4,7 @@ use Symfony\Component\HttpKernel\Bundle\Bundle; +/** @internal */ final class JsonRpcTestBundle extends Bundle { } diff --git a/Test/Tests/EntityConversionTest.php b/Test/Tests/EntityConversionTest.php index 78f2248..952a11d 100644 --- a/Test/Tests/EntityConversionTest.php +++ b/Test/Tests/EntityConversionTest.php @@ -59,14 +59,22 @@ public function testPrivateEntityFields() $jsonResponse = new SyncResponse($content); self::assertTrue($jsonResponse->isSuccessful(), json_encode($content, JSON_PRETTY_PRINT)); self::assertTrue(isset($jsonResponse->getBody()->{'sample_payload'}), json_encode($content, JSON_PRETTY_PRINT)); - self::assertEquals('my-payload', $jsonResponse->getBody()->{'sample_payload'}, json_encode($content, JSON_PRETTY_PRINT)); + self::assertEquals( + 'my-payload', + $jsonResponse->getBody()->{'sample_payload'}, + json_encode($content, JSON_PRETTY_PRINT) + ); if (static::$kernel->getContainer()->has('jms_serializer')) { self::assertTrue( isset($jsonResponse->getBody()->{'private_payload'}), json_encode($content, JSON_PRETTY_PRINT) ); - self::assertEquals('secret-payload', $jsonResponse->getBody()->{'private_payload'}, json_encode($content, JSON_PRETTY_PRINT)); + self::assertEquals( + 'secret-payload', + $jsonResponse->getBody()->{'private_payload'}, + json_encode($content, JSON_PRETTY_PRINT) + ); } } @@ -92,14 +100,22 @@ public function testNestedContext() $jsonResponse = new SyncResponse($content); self::assertTrue($jsonResponse->isSuccessful(), json_encode($content, JSON_PRETTY_PRINT)); self::assertTrue(isset($jsonResponse->getBody()->{'sample_payload'}), json_encode($content, JSON_PRETTY_PRINT)); - self::assertEquals('my-payload', $jsonResponse->getBody()->{'sample_payload'}, json_encode($content, JSON_PRETTY_PRINT)); + self::assertEquals( + 'my-payload', + $jsonResponse->getBody()->{'sample_payload'}, + json_encode($content, JSON_PRETTY_PRINT) + ); if (static::$kernel->getContainer()->has('jms_serializer')) { self::assertTrue( isset($jsonResponse->getBody()->{'private_payload'}), json_encode($content, JSON_PRETTY_PRINT) ); - self::assertEquals('secret-payload', $jsonResponse->getBody()->{'private_payload'}, json_encode($content, JSON_PRETTY_PRINT)); + self::assertEquals( + 'secret-payload', + $jsonResponse->getBody()->{'private_payload'}, + json_encode($content, JSON_PRETTY_PRINT) + ); } } } diff --git a/Test/Tests/SampleControllerTest.php b/Test/Tests/SampleControllerTest.php index 3b39cfb..9163f9c 100644 --- a/Test/Tests/SampleControllerTest.php +++ b/Test/Tests/SampleControllerTest.php @@ -57,7 +57,14 @@ public function testArrayRequest() $jsonResponse = new SyncResponse($content); self::assertTrue($jsonResponse->isSuccessful(), json_encode($content)); - self::assertTrue(isset($jsonResponse->getBody()[0]->{'sample_payload'}), json_encode($content, JSON_PRETTY_PRINT)); - self::assertEquals('my-payload', $jsonResponse->getBody()[0]->{'sample_payload'}, json_encode($content, JSON_PRETTY_PRINT)); + self::assertTrue( + isset($jsonResponse->getBody()[0]->{'sample_payload'}), + json_encode($content, JSON_PRETTY_PRINT) + ); + self::assertEquals( + 'my-payload', + $jsonResponse->getBody()[0]->{'sample_payload'}, + json_encode($content, JSON_PRETTY_PRINT) + ); } } diff --git a/Tests/JsonRpcControllerTest.php b/Tests/JsonRpcControllerTest.php new file mode 100644 index 0000000..a93f5c9 --- /dev/null +++ b/Tests/JsonRpcControllerTest.php @@ -0,0 +1,238 @@ +createController(); + + $request = $this->createJsonRequest('/', []); + $response = $controller->jsonRpcAction($request); + + self::assertInstanceOf(JsonRpcHttpResponse::class, $response); + self::assertTrue($response->isSuccessful()); + self::assertEquals('[]', $response->getContent()); + } + + public function getInvalidJsonRequests() + { + return [ + 'empty' => [null], + 'string' => ['test'], + 'invalid_json' => [substr(json_encode(['test']), 2)], + ]; + } + + /** + * @dataProvider getInvalidJsonRequests + * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException + * + * @param $content + */ + public function testInvalidJsonHandling($content) + { + $controller = $this->createController(); + + $request = $this->createJsonRequest('/', $content); + $controller->jsonRpcAction($request); + } + + public function getInvalidJsonRpcRequests() + { + return [ + 'invalid version' => [['jsonrpc' => '1.0']], + 'no method' => [['jsonrpc' => '2.0']], + 'no version' => [['method' => 'test']], + ]; + } + + /** + * @dataProvider getInvalidJsonRpcRequests + * @expectedException \Bankiru\Api\JsonRpc\Exception\InvalidRequestException + * + * @param $content + */ + public function testInvalidJsonRpcHandling($content) + { + $controller = $this->createController(); + + $request = $this->createJsonRequest('/', $content); + $controller->jsonRpcAction($request); + } + + public function testSingleRequestHandling() + { + $controller = $this->createController(); + $request = $this->createJsonRequest( + '/', + [ + 'jsonrpc' => '2.0', + 'id' => 'test', + 'method' => 'test', + ] + ); + $response = $controller->jsonRpcAction($request); + + self::assertTrue($response->isSuccessful()); + self::assertEquals('{"jsonrpc":"2.0","id":"test","result":{"success":true}}', $response->getContent()); + } + + public function testBatchRequestHandling() + { + $controller = $this->createController(); + $request = $this->createJsonRequest( + '/', + [ + [ + 'jsonrpc' => '2.0', + 'id' => 'test', + 'method' => 'test', + ], + ] + ); + $response = $controller->jsonRpcAction($request); + + self::assertTrue($response->isSuccessful()); + self::assertEquals('[{"jsonrpc":"2.0","id":"test","result":{"success":true}}]', $response->getContent()); + } + + public function testExceptionHandling() + { + $controller = $this->createController(); + $request = $this->createJsonRequest( + '/', + [ + 'jsonrpc' => '2.0', + 'id' => 'test', + 'method' => 'exception', + ] + ); + $response = $controller->jsonRpcAction($request); + + self::assertTrue($response->isSuccessful()); + self::assertEquals( + '{"jsonrpc":"2.0","id":"test","error":{"code":-32603,"message":"Failure!","data":null}}', + $response->getContent() + ); + } + + public function testExceptionHandlingAmongBatchRequest() + { + $controller = $this->createController(); + $request = $this->createJsonRequest( + '/', + [ + [ + 'jsonrpc' => '2.0', + 'id' => 'test1', + 'method' => 'test', + ], + [ + 'jsonrpc' => '2.0', + 'id' => 'test2', + 'method' => 'exception', + ], + ] + ); + $response = $controller->jsonRpcAction($request); + + self::assertTrue($response->isSuccessful()); + self::assertEquals( + '[{"jsonrpc":"2.0","id":"test1","result":{"success":true}},{"jsonrpc":"2.0","id":"test2","error":{"code":-32603,"message":"Failure!","data":null}}]', + $response->getContent() + ); + } + + private function createJsonRequest($uri, $content) + { + return Request::create($uri, 'POST', [], [], [], [], json_encode($content)); + } + + private function getContainerMock() + { + $mock = $this->prophesize(ContainerInterface::class); + $kernel = $this->prophesize(KernelInterface::class); + $resolver = $this->prophesize(ControllerResolverInterface::class); + $resolver->getController(Argument::type(RpcRequestInterface::class))->willReturn( + function (JsonRpcRequestInterface $request) { + if ($request->getMethod() === 'exception') { + throw new \LogicException('Failure!'); + } + + return new JsonRpcResponse($request->getId(), ['success' => true]); + } + ); + $resolver->getArguments(Argument::type(RpcRequestInterface::class), Argument::any())->will( + function (array $args) { + return [ + $args[0], + ]; + } + ); + + $evm = $this->prophesize(EventDispatcherInterface::class); + $evm->dispatch(Argument::exact(RpcEvents::EXCEPTION), Argument::type(GetExceptionResponseEvent::class)) + ->will( + function ($args) { + /** @var GetExceptionResponseEvent $event */ + $event = $args[1]; + + /** @var JsonRpcRequestInterface $request */ + $request = $event->getRequest(); + + $event->setResponse( + new JsonRpcResponse( + $request->getId(), + null, new JsonRpcError( + JsonRpcError::INTERNAL_ERROR, + $event->getException()->getMessage() + ) + ) + ); + } + ); + $evm->dispatch(Argument::exact(RpcEvents::FINISH_REQUEST), Argument::type(FinishRequestEvent::class)) + ->willReturn(null); + $evm->dispatch(Argument::exact(RpcEvents::CONTROLLER), Argument::type(FilterControllerEvent::class)) + ->willReturn(null); + $evm->dispatch(Argument::exact(RpcEvents::REQUEST), Argument::type(GetResponseEvent::class))->willReturn(null); + $evm->dispatch(Argument::exact(RpcEvents::RESPONSE), Argument::type(FilterResponseEvent::class)) + ->willReturn(null); + + $mock->get(Argument::exact('jsonrpc.controller_resolver'))->willReturn($resolver->reveal()); + $mock->get(Argument::exact('event_dispatcher'))->willReturn($evm->reveal()); + $mock->get(Argument::exact('kernel'))->willReturn($kernel->reveal()); + + return $mock->reveal(); + } + + private function createController() + { + $controller = new JsonRpcController(); + $controller->setContainer($this->getContainerMock()); + + return $controller; + } +} diff --git a/bootstrap.php b/bootstrap.php index 8c57a77..445fa3b 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -1,12 +1,12 @@ Date: Wed, 19 Jul 2017 11:27:57 +0300 Subject: [PATCH 10/29] Fix excepted message --- Test/Tests/AnnotatedControllerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/Tests/AnnotatedControllerTest.php b/Test/Tests/AnnotatedControllerTest.php index d14a614..95dc5f0 100644 --- a/Test/Tests/AnnotatedControllerTest.php +++ b/Test/Tests/AnnotatedControllerTest.php @@ -33,7 +33,7 @@ public function testNestedContext() /** * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException - * @expectedExceptionMessage Not an valid JSON request + * @expectedExceptionMessage Not a valid JSON-RPC request */ public function testEmptyRequest() { From b61a867df005e5e865d31669591fb7089429a844 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Wed, 19 Jul 2017 11:44:05 +0300 Subject: [PATCH 11/29] Readme improvements --- README.md | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4d772a0..20c59ba 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,7 @@ # JSON-RPC Server bundle -This bundle provides JSON-RPC 2.0 server ontop of the `bankiru/rpc-server-bundle` library. - +This bundle provides JSON-RPC 2.0 server on top of the `bankiru/rpc-server-bundle` library. ## Serialization @@ -20,22 +19,28 @@ was instance of `JsonRpcRequestInterface` and the response is not a `JsonRpcResponseInterface` object. You can bypass the serialization process with sending pre-created response object or implementing your own view listener -### Context +### `json_encode` serializer listener (Default) + +Default serialization process uses `json_encode` function to convert the response data into string representation. +Implement `\JsonSerializable` to configure the structure of the response + +### JMS Serializer listener (Optional) -This library utilizes [JMS Serializer Bundle](https://github.com/schmittjoh/JMSSerializerBundle) +This library can utilize [JMS Serializer Bundle](https://github.com/schmittjoh/JMSSerializerBundle) to automatically serialize non-JSON-RPC controller response to serialized view. You can specify the serialization context at the routing level. Also you could disable default context usage with `with_default_context: false` +Proceed to [official documentation](http://jmsyst.com/bundles/JMSSerializerBundle) for more information + ## Exception handling Any unhandled exception from the controller would be automatically converted to the proper JSON-RPC failure response with INTERNAL_ERROR (-32603) code. -If you want do display other error you could extend `JsonRpcException` class or +If you want do display custom error you could extend `JsonRpcException` class or configure it manually like following: ```php - $exception = new JsonRpcException(); $execption->setJsonRpcError( new JsonRpcError( @@ -44,9 +49,31 @@ $execption->setJsonRpcError( (object)['debug_data' => 'some debug data'] ) ); +``` + +Some exceptions are handled in a more specific manner + +* `AccessDeniedException` will result in `-32601` (method not found) json-rpc error code. +* `MethodNotFoundException` will result in `-32601` (method not found) json-rpc error code. +* `InvalidMethodParametersException` will result in `-32602` (invalid params) json-rpc error code + +## Configuration + +Configuration reference: + +```yaml +jsonrpc_server: + + # View listener service ID + view_listener: jsonrpc.builtin_adapter.view_listener + adapters: + jms: + relation_handlers: + # Prototype: Key: Relation handler name (i.e. "Relation"), Value: service ID for relation handler entity manager + handler: ~ ``` ## Specification -Refer to official JSON-RPC 2.0 specification at http://www.jsonrpc.org/specification +Refer to official JSON-RPC 2.0 specification at http://www.jsonrpc.org/specification From eb04955cc1ebaafd5b5f02ee9caccf983dc8b713 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Wed, 19 Jul 2017 11:58:32 +0300 Subject: [PATCH 12/29] Readme improvements, ID identifiers renamed --- .travis.yml | 8 ++++---- Controller/JsonRpcController.php | 2 +- DependencyInjection/Configuration.php | 2 +- README.md | 2 +- Resources/config/adapters/builtin.yml | 2 +- Resources/config/adapters/jms.yml | 2 +- Resources/config/jsonrpc.yml | 20 ++++++++++---------- Resources/config/security.yml | 2 +- Test/Kernel/config_jms.yml | 2 +- Tests/JsonRpcControllerTest.php | 2 +- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0704a17..14781d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ cache: - $HOME/.composer/cache env: - - PACKAGES='symfony/symfony=~3.4@dev' + - PACKAGES='symfony/symfony=~3.4' STABILITY=dev - PACKAGES='symfony/symfony=3.3.*' - PACKAGES='symfony/symfony=3.2.*' - PACKAGES='symfony/symfony=2.8.*' @@ -26,13 +26,13 @@ matrix: fast_finish: true include: - php: 7.1 - env: PACKAGES='symfony/symfony=~4.0@dev' STABILITY=dev + env: PACKAGES='symfony/symfony=~4.0' STABILITY=dev - php: nightly - env: PACKAGES='symfony/symfony=~4.0@dev' STABILITY=dev + env: PACKAGES='symfony/symfony=~4.0' STABILITY=dev allow_failures: - php: nightly - - env: PACKAGES='symfony/symfony=~4.0@dev' STABILITY=dev + - env: PACKAGES='symfony/symfony=~4.0' STABILITY=dev before_install: - travis_retry composer self-update diff --git a/Controller/JsonRpcController.php b/Controller/JsonRpcController.php index 7770bed..8d04266 100644 --- a/Controller/JsonRpcController.php +++ b/Controller/JsonRpcController.php @@ -64,7 +64,7 @@ public function jsonRpcAction(Request $request) */ protected function getResolver() { - return $this->get('jsonrpc.controller_resolver'); + return $this->get('jsonrpc_server.controller_resolver'); } /** diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 7b161d9..853c1c9 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -17,7 +17,7 @@ public function getConfigTreeBuilder() $root->children() ->scalarNode('view_listener') ->info('View listener service ID') - ->defaultValue('jsonrpc.builtin_adapter.view_listener'); + ->defaultValue('jsonrpc_server.builtin_adapter.view_listener'); $adapters = $root->children()->arrayNode('adapters'); $this->configureJms($adapters); diff --git a/README.md b/README.md index 20c59ba..71a3e3b 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Configuration reference: jsonrpc_server: # View listener service ID - view_listener: jsonrpc.builtin_adapter.view_listener + view_listener: jsonrpc_server.builtin_adapter.view_listener adapters: jms: relation_handlers: diff --git a/Resources/config/adapters/builtin.yml b/Resources/config/adapters/builtin.yml index 9a1b405..08b3b2e 100644 --- a/Resources/config/adapters/builtin.yml +++ b/Resources/config/adapters/builtin.yml @@ -1,4 +1,4 @@ services: - jsonrpc.builtin_adapter.view_listener: + jsonrpc_server.builtin_adapter.view_listener: class: Bankiru\Api\JsonRpc\Adapters\Builtin\JsonEncodeViewListener public: false diff --git a/Resources/config/adapters/jms.yml b/Resources/config/adapters/jms.yml index d396920..188db89 100644 --- a/Resources/config/adapters/jms.yml +++ b/Resources/config/adapters/jms.yml @@ -1,5 +1,5 @@ services: - jsonrpc.jms_adapter.view_listener: + jsonrpc_server.jms_adapter.view_listener: class: Bankiru\Api\JsonRpc\Adapters\JMS\SerializedJsonRpcResponseListener arguments: - "@jms_serializer" diff --git a/Resources/config/jsonrpc.yml b/Resources/config/jsonrpc.yml index f4534d9..918196a 100644 --- a/Resources/config/jsonrpc.yml +++ b/Resources/config/jsonrpc.yml @@ -1,42 +1,42 @@ parameters: - jsonrpc.debug: false + jsonrpc_server.debug: false services: - jsonrpc.controller_resolver: + jsonrpc_server.controller_resolver: class: Bankiru\Api\Rpc\Routing\ControllerResolver\ControllerResolver arguments: - "@service_container" - - "@jsonrpc.controller_name_parser" + - "@jsonrpc_server.controller_name_parser" - "@?logger" - jsonrpc.view.notification_response: + jsonrpc_server.view.notification_response: class: Bankiru\Api\JsonRpc\Listener\NotificationResponseListener tags: - { name: kernel.event_listener, event: rpc.view, method: onNullResponse, priority: -255 } - jsonrpc.view.notification_filter: + jsonrpc_server.view.notification_filter: class: Bankiru\Api\JsonRpc\Listener\NotificationFilter tags: - { name: kernel.event_listener, event: rpc.response, method: filterNotificationResponse, priority: -255 } - jsonrpc.reponse_listener.match_id: + jsonrpc_server.reponse_listener.match_id: class: Bankiru\Api\JsonRpc\Listener\IdMatcherListener tags: - { name: kernel.event_listener, event: rpc.response, method: onFilterResponse, priority: -254 } - jsonrpc.controller_name_parser: + jsonrpc_server.controller_name_parser: class: Bankiru\Api\JsonRpc\Controller\JsonRpcControllerNameParser parent: rpc.controller_name_parser - jsonrpc.exception_listener: + jsonrpc_server.exception_listener: class: Bankiru\Api\JsonRpc\Listener\ExceptionHandlerListener arguments: - - "%jsonrpc.debug%" + - "%jsonrpc_server.debug%" tags: - { name: kernel.event_listener, event: rpc.exception, method: onJsonRpcException } - jsonrpc.http_exception_listener: + jsonrpc_server.http_exception_listener: class: Bankiru\Api\JsonRpc\Listener\HttpExceptionListener tags: - { name: kernel.event_listener, event: kernel.exception, method: onJsonRpcException } diff --git a/Resources/config/security.yml b/Resources/config/security.yml index 1e766e1..46a93ea 100644 --- a/Resources/config/security.yml +++ b/Resources/config/security.yml @@ -1,5 +1,5 @@ services: - jsonrpc.security.exception_listener: + jsonrpc_server.security.exception_listener: class: Bankiru\Api\JsonRpc\Listener\AccessDeniedExceptionListener tags: - { name: kernel.event_listener, event: rpc.exception, method: onRpcException, priority: 255 } diff --git a/Test/Kernel/config_jms.yml b/Test/Kernel/config_jms.yml index d5984f6..211a1a1 100644 --- a/Test/Kernel/config_jms.yml +++ b/Test/Kernel/config_jms.yml @@ -6,4 +6,4 @@ jms_serializer: auto_detection: true jsonrpc_server: - view_listener: jsonrpc.jms_adapter.view_listener + view_listener: jsonrpc_server.jms_adapter.view_listener diff --git a/Tests/JsonRpcControllerTest.php b/Tests/JsonRpcControllerTest.php index a93f5c9..e7729c9 100644 --- a/Tests/JsonRpcControllerTest.php +++ b/Tests/JsonRpcControllerTest.php @@ -221,7 +221,7 @@ function ($args) { $evm->dispatch(Argument::exact(RpcEvents::RESPONSE), Argument::type(FilterResponseEvent::class)) ->willReturn(null); - $mock->get(Argument::exact('jsonrpc.controller_resolver'))->willReturn($resolver->reveal()); + $mock->get(Argument::exact('jsonrpc_server.controller_resolver'))->willReturn($resolver->reveal()); $mock->get(Argument::exact('event_dispatcher'))->willReturn($evm->reveal()); $mock->get(Argument::exact('kernel'))->willReturn($kernel->reveal()); From be63197473c1da6c589acd75e0108089b3a47d79 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Wed, 19 Jul 2017 12:05:04 +0300 Subject: [PATCH 13/29] Dev deps updates --- Resources/config/jsonrpc.yml | 2 +- Test/Kernel/config.yml | 2 +- Test/Tests/RouterTest.php | 2 +- Test/Tests/SecurityTest.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Resources/config/jsonrpc.yml b/Resources/config/jsonrpc.yml index 918196a..2869642 100644 --- a/Resources/config/jsonrpc.yml +++ b/Resources/config/jsonrpc.yml @@ -27,7 +27,7 @@ services: jsonrpc_server.controller_name_parser: class: Bankiru\Api\JsonRpc\Controller\JsonRpcControllerNameParser - parent: rpc.controller_name_parser + parent: rpc_server.controller_name_parser jsonrpc_server.exception_listener: class: Bankiru\Api\JsonRpc\Listener\ExceptionHandlerListener diff --git a/Test/Kernel/config.yml b/Test/Kernel/config.yml index 7d3dbf1..1767601 100644 --- a/Test/Kernel/config.yml +++ b/Test/Kernel/config.yml @@ -26,7 +26,7 @@ jsonrpc_server: relation_handlers: Relation: doctrine.orm.entity_manager -rpc: +rpc_server: router: endpoints: test: diff --git a/Test/Tests/RouterTest.php b/Test/Tests/RouterTest.php index 40ab9c6..a853ca8 100644 --- a/Test/Tests/RouterTest.php +++ b/Test/Tests/RouterTest.php @@ -25,7 +25,7 @@ public function testRpcRouterCollection() foreach (['test', 'test_private'] as $endpoint) /** @var MethodCollection $collection */ { /** @var Router $router */ - $router = $client->getContainer()->get('rpc.endpoint_router.' . $endpoint); + $router = $client->getContainer()->get('rpc_server.endpoint_router.' . $endpoint); self::assertNotNull($router); $collection = $router->getMethodCollection(); self::assertNotNull($router); diff --git a/Test/Tests/SecurityTest.php b/Test/Tests/SecurityTest.php index 3d431b9..3e854e2 100644 --- a/Test/Tests/SecurityTest.php +++ b/Test/Tests/SecurityTest.php @@ -2,7 +2,7 @@ namespace Bankiru\Api\JsonRpc\Test\Tests; -use Bankiru\Api\Rpc\Tests\Fixtures\Impl\Request; +use Bankiru\Api\Rpc\Test\Impl\Request; use ScayTrase\Api\JsonRpc\JsonRpcError; final class SecurityTest extends JsonRpcTestCase From 08e4c1471db310b586f49211091a2f782000acf5 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Thu, 20 Jul 2017 09:34:48 +0300 Subject: [PATCH 14/29] CS Fixes (scrutinizer) --- DependencyInjection/BankiruJsonRpcServerExtension.php | 1 - Test/Tests/RouterTest.php | 2 +- Tests/JsonRpcControllerTest.php | 6 ++++++ Tests/JsonRpcHttpResponseTest.php | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/DependencyInjection/BankiruJsonRpcServerExtension.php b/DependencyInjection/BankiruJsonRpcServerExtension.php index a96d42c..adc168f 100644 --- a/DependencyInjection/BankiruJsonRpcServerExtension.php +++ b/DependencyInjection/BankiruJsonRpcServerExtension.php @@ -2,7 +2,6 @@ namespace Bankiru\Api\JsonRpc\DependencyInjection; -use Bankiru\Api\JsonRpc\Adapters\JMS\Compiler\JmsDriverPass; use Bankiru\Api\JsonRpc\Adapters\JMS\Compiler\RelationHandlerHelper; use Bankiru\Api\Rpc\RpcEvents; use JMS\SerializerBundle\JMSSerializerBundle; diff --git a/Test/Tests/RouterTest.php b/Test/Tests/RouterTest.php index a853ca8..4c56bab 100644 --- a/Test/Tests/RouterTest.php +++ b/Test/Tests/RouterTest.php @@ -23,7 +23,7 @@ public function testRpcRouterCollection() { $client = self::createClient(); - foreach (['test', 'test_private'] as $endpoint) /** @var MethodCollection $collection */ { + foreach (['test', 'test_private'] as $endpoint) { /** @var Router $router */ $router = $client->getContainer()->get('rpc_server.endpoint_router.' . $endpoint); self::assertNotNull($router); diff --git a/Tests/JsonRpcControllerTest.php b/Tests/JsonRpcControllerTest.php index e7729c9..4b6741b 100644 --- a/Tests/JsonRpcControllerTest.php +++ b/Tests/JsonRpcControllerTest.php @@ -165,6 +165,12 @@ public function testExceptionHandlingAmongBatchRequest() ); } + /** + * @param string $uri + * @param mixed $content + * + * @return Request + */ private function createJsonRequest($uri, $content) { return Request::create($uri, 'POST', [], [], [], [], json_encode($content)); diff --git a/Tests/JsonRpcHttpResponseTest.php b/Tests/JsonRpcHttpResponseTest.php index 5893f6a..dad36f4 100644 --- a/Tests/JsonRpcHttpResponseTest.php +++ b/Tests/JsonRpcHttpResponseTest.php @@ -24,7 +24,7 @@ public function getResponseVariants() 'empty array' => [[]], 'single success' => [clone $success], 'single failure' => [clone $error], - 'mixed array' => [[clone $success, clone $error,]], + 'mixed array' => [[clone $success, clone $error]], ]; } From 5be922d0b202b64811d5bad8fd0cd53074f59d77 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Thu, 20 Jul 2017 09:39:19 +0300 Subject: [PATCH 15/29] Code Fixes (scrutinizer) --- .../BankiruJsonRpcServerExtension.php | 11 ++-- Tests/JsonRpcControllerTest.php | 55 +++++++++++-------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/DependencyInjection/BankiruJsonRpcServerExtension.php b/DependencyInjection/BankiruJsonRpcServerExtension.php index adc168f..4588b9d 100644 --- a/DependencyInjection/BankiruJsonRpcServerExtension.php +++ b/DependencyInjection/BankiruJsonRpcServerExtension.php @@ -21,8 +21,8 @@ public function load(array $configs, ContainerBuilder $container) $config = $this->processConfiguration(new Configuration(), $configs); - $this->configureSecurity($container, $config); - $this->configureBuiltinAdapter($container, $config); + $this->configureSecurity($container); + $this->configureBuiltinAdapter($container); $this->configureJmsAdapter($container, $config); if (!empty($config['view_listener'])) { @@ -47,7 +47,7 @@ public function getAlias() /** * @param ContainerBuilder $container */ - private function configureBuiltinAdapter(ContainerBuilder $container, array $config) + private function configureBuiltinAdapter(ContainerBuilder $container) { $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config/adapters')); @@ -56,6 +56,7 @@ private function configureBuiltinAdapter(ContainerBuilder $container, array $con /** * @param ContainerBuilder $container + * @param array $config */ private function configureJmsAdapter(ContainerBuilder $container, array $config) { @@ -66,7 +67,7 @@ private function configureJmsAdapter(ContainerBuilder $container, array $config) $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config/adapters')); $loader->load('jms.yml'); - foreach ($config['adapters']['jms']['relation_handlers'] as $handler => $emid) { + foreach ((array)$config['adapters']['jms']['relation_handlers'] as $handler => $emid) { RelationHandlerHelper::ConfigureRelationHandler($container, $handler, $emid); } } @@ -74,7 +75,7 @@ private function configureJmsAdapter(ContainerBuilder $container, array $config) /** * @param ContainerBuilder $container */ - private function configureSecurity(ContainerBuilder $container, array $config) + private function configureSecurity(ContainerBuilder $container) { if (in_array(SecurityBundle::class, $container->getParameter('kernel.bundles'), true)) { $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); diff --git a/Tests/JsonRpcControllerTest.php b/Tests/JsonRpcControllerTest.php index 4b6741b..93ea7fb 100644 --- a/Tests/JsonRpcControllerTest.php +++ b/Tests/JsonRpcControllerTest.php @@ -82,40 +82,47 @@ public function testInvalidJsonRpcHandling($content) $controller->jsonRpcAction($request); } - public function testSingleRequestHandling() + public function getValidResponseAndRequests() { - $controller = $this->createController(); - $request = $this->createJsonRequest( - '/', - [ - 'jsonrpc' => '2.0', - 'id' => 'test', - 'method' => 'test', - ] - ); - $response = $controller->jsonRpcAction($request); - - self::assertTrue($response->isSuccessful()); - self::assertEquals('{"jsonrpc":"2.0","id":"test","result":{"success":true}}', $response->getContent()); + return [ + 'single' => [ + [ + 'jsonrpc' => '2.0', + 'id' => 'test', + 'method' => 'test', + ], + '{"jsonrpc":"2.0","id":"test","result":{"success":true}}', + ], + 'batch' => [ + [ + [ + 'jsonrpc' => '2.0', + 'id' => 'test', + 'method' => 'test', + ], + ], + '[{"jsonrpc":"2.0","id":"test","result":{"success":true}}]', + ], + ]; } - public function testBatchRequestHandling() + /** + * @dataProvider getValidResponseAndRequests + * + * @param array $content + * @param string $responseBody + */ + public function testValidRequestHandling($content, $responseBody) { $controller = $this->createController(); $request = $this->createJsonRequest( '/', - [ - [ - 'jsonrpc' => '2.0', - 'id' => 'test', - 'method' => 'test', - ], - ] + $content ); $response = $controller->jsonRpcAction($request); self::assertTrue($response->isSuccessful()); - self::assertEquals('[{"jsonrpc":"2.0","id":"test","result":{"success":true}}]', $response->getContent()); + self::assertEquals($responseBody, $response->getContent()); } public function testExceptionHandling() @@ -187,7 +194,7 @@ function (JsonRpcRequestInterface $request) { throw new \LogicException('Failure!'); } - return new JsonRpcResponse($request->getId(), ['success' => true]); + return new JsonRpcResponse($request->getId(), (object)['success' => true]); } ); $resolver->getArguments(Argument::type(RpcRequestInterface::class), Argument::any())->will( From 62c9f54409717e762d269f314cfd03d9bbebfbae Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Thu, 20 Jul 2017 09:56:13 +0300 Subject: [PATCH 16/29] Duplication fix (scrutinizer) --- Tests/JsonRpcControllerTest.php | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Tests/JsonRpcControllerTest.php b/Tests/JsonRpcControllerTest.php index 93ea7fb..76a8401 100644 --- a/Tests/JsonRpcControllerTest.php +++ b/Tests/JsonRpcControllerTest.php @@ -24,18 +24,6 @@ final class JsonRpcControllerTest extends TestCase { - public function testEmptyBatchRequestHandling() - { - $controller = $this->createController(); - - $request = $this->createJsonRequest('/', []); - $response = $controller->jsonRpcAction($request); - - self::assertInstanceOf(JsonRpcHttpResponse::class, $response); - self::assertTrue($response->isSuccessful()); - self::assertEquals('[]', $response->getContent()); - } - public function getInvalidJsonRequests() { return [ @@ -85,7 +73,8 @@ public function testInvalidJsonRpcHandling($content) public function getValidResponseAndRequests() { return [ - 'single' => [ + 'empty batch' => [[], '[]'], + 'single' => [ [ 'jsonrpc' => '2.0', 'id' => 'test', @@ -93,7 +82,7 @@ public function getValidResponseAndRequests() ], '{"jsonrpc":"2.0","id":"test","result":{"success":true}}', ], - 'batch' => [ + 'batch' => [ [ [ 'jsonrpc' => '2.0', @@ -121,6 +110,7 @@ public function testValidRequestHandling($content, $responseBody) ); $response = $controller->jsonRpcAction($request); + self::assertInstanceOf(JsonRpcHttpResponse::class, $response); self::assertTrue($response->isSuccessful()); self::assertEquals($responseBody, $response->getContent()); } From 32037649759bee671fc2d547d8f8a32e046b4c6a Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Thu, 20 Jul 2017 10:24:20 +0300 Subject: [PATCH 17/29] Code fixes (insight) --- .gitignore | 6 ------ Adapters/JMS/Compiler/RelationHandlerHelper.php | 2 +- DependencyInjection/BankiruJsonRpcServerExtension.php | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 389e076..178ff43 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,4 @@ -/.idea /vendor/ - -# Build data /build/ /target/ - -# Any PHARs -*.phar composer.lock diff --git a/Adapters/JMS/Compiler/RelationHandlerHelper.php b/Adapters/JMS/Compiler/RelationHandlerHelper.php index c4f18cc..4f43d6d 100644 --- a/Adapters/JMS/Compiler/RelationHandlerHelper.php +++ b/Adapters/JMS/Compiler/RelationHandlerHelper.php @@ -8,7 +8,7 @@ final class RelationHandlerHelper { - public static function ConfigureRelationHandler(ContainerBuilder $builder, $handler, $emid) + public static function configureRelationHandler(ContainerBuilder $builder, $handler, $emid) { if (!$builder->has($emid)) { return; diff --git a/DependencyInjection/BankiruJsonRpcServerExtension.php b/DependencyInjection/BankiruJsonRpcServerExtension.php index 4588b9d..ecb28b6 100644 --- a/DependencyInjection/BankiruJsonRpcServerExtension.php +++ b/DependencyInjection/BankiruJsonRpcServerExtension.php @@ -68,7 +68,7 @@ private function configureJmsAdapter(ContainerBuilder $container, array $config) $loader->load('jms.yml'); foreach ((array)$config['adapters']['jms']['relation_handlers'] as $handler => $emid) { - RelationHandlerHelper::ConfigureRelationHandler($container, $handler, $emid); + RelationHandlerHelper::configureRelationHandler($container, $handler, $emid); } } From 1ca75e9a45330e24189220da786d0704080d7f1d Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Thu, 27 Jul 2017 17:46:05 +0300 Subject: [PATCH 18/29] Fix handlers loading process --- ...nHandlerHelper.php => RelationHandlerPass.php} | 15 ++++++++++++--- BankiruJsonRpcServerBundle.php | 8 ++++++++ .../BankiruJsonRpcServerExtension.php | 5 +---- 3 files changed, 21 insertions(+), 7 deletions(-) rename Adapters/JMS/Compiler/{RelationHandlerHelper.php => RelationHandlerPass.php} (74%) diff --git a/Adapters/JMS/Compiler/RelationHandlerHelper.php b/Adapters/JMS/Compiler/RelationHandlerPass.php similarity index 74% rename from Adapters/JMS/Compiler/RelationHandlerHelper.php rename to Adapters/JMS/Compiler/RelationHandlerPass.php index 4f43d6d..56c598d 100644 --- a/Adapters/JMS/Compiler/RelationHandlerHelper.php +++ b/Adapters/JMS/Compiler/RelationHandlerPass.php @@ -3,18 +3,27 @@ namespace Bankiru\Api\JsonRpc\Adapters\JMS\Compiler; use Bankiru\Api\JsonRpc\Adapters\JMS\RelationsHandler; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; -final class RelationHandlerHelper +final class RelationHandlerPass implements CompilerPassInterface { - public static function configureRelationHandler(ContainerBuilder $builder, $handler, $emid) + /** {@inheritdoc} */ + public function process(ContainerBuilder $container) + { + foreach ($container->getParameter('jsonrpc_server.handlers') as $handler => $emid) { + $this->configureRelationHandler($container, $handler, $emid); + } + } + + private function configureRelationHandler(ContainerBuilder $builder, $handler, $emid) { if (!$builder->has($emid)) { return; } - $builder->register('jms_serializer.handler.relation', RelationsHandler::class) + $builder->register('jms_serializer.handler.relation.' . $handler, RelationsHandler::class) ->setArguments([new Reference($emid)]) ->addTag( 'jms_serializer.handler', diff --git a/BankiruJsonRpcServerBundle.php b/BankiruJsonRpcServerBundle.php index 11c0a8c..e894d1b 100644 --- a/BankiruJsonRpcServerBundle.php +++ b/BankiruJsonRpcServerBundle.php @@ -2,13 +2,21 @@ namespace Bankiru\Api\JsonRpc; +use Bankiru\Api\JsonRpc\Adapters\JMS\Compiler\RelationHandlerPass; use Bankiru\Api\JsonRpc\DependencyInjection\BankiruJsonRpcServerExtension; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; final class BankiruJsonRpcServerBundle extends Bundle { const JSONRPC_VERSION = '2.0'; + public function build(ContainerBuilder $container) + { + parent::build($container); + $container->addCompilerPass(new RelationHandlerPass()); + } + public function getContainerExtension() { return new BankiruJsonRpcServerExtension(); diff --git a/DependencyInjection/BankiruJsonRpcServerExtension.php b/DependencyInjection/BankiruJsonRpcServerExtension.php index ecb28b6..4f8fe21 100644 --- a/DependencyInjection/BankiruJsonRpcServerExtension.php +++ b/DependencyInjection/BankiruJsonRpcServerExtension.php @@ -2,7 +2,6 @@ namespace Bankiru\Api\JsonRpc\DependencyInjection; -use Bankiru\Api\JsonRpc\Adapters\JMS\Compiler\RelationHandlerHelper; use Bankiru\Api\Rpc\RpcEvents; use JMS\SerializerBundle\JMSSerializerBundle; use Symfony\Bundle\SecurityBundle\SecurityBundle; @@ -67,9 +66,7 @@ private function configureJmsAdapter(ContainerBuilder $container, array $config) $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config/adapters')); $loader->load('jms.yml'); - foreach ((array)$config['adapters']['jms']['relation_handlers'] as $handler => $emid) { - RelationHandlerHelper::configureRelationHandler($container, $handler, $emid); - } + $container->setParameter('jsonrpc_server.handlers', (array)$config['adapters']['jms']['relation_handlers']); } /** From 898b753246cfe44c61f35792a678f60098e39f59 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Fri, 28 Jul 2017 08:38:29 +0300 Subject: [PATCH 19/29] Fix DI extension --- Adapters/JMS/Compiler/RelationHandlerPass.php | 6 +++++- DependencyInjection/BankiruJsonRpcServerExtension.php | 6 +++++- DependencyInjection/Configuration.php | 2 ++ Resources/config/adapters/jms.yml | 3 +++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Adapters/JMS/Compiler/RelationHandlerPass.php b/Adapters/JMS/Compiler/RelationHandlerPass.php index 56c598d..7b3d5f3 100644 --- a/Adapters/JMS/Compiler/RelationHandlerPass.php +++ b/Adapters/JMS/Compiler/RelationHandlerPass.php @@ -12,7 +12,11 @@ final class RelationHandlerPass implements CompilerPassInterface /** {@inheritdoc} */ public function process(ContainerBuilder $container) { - foreach ($container->getParameter('jsonrpc_server.handlers') as $handler => $emid) { + if (!$container->hasParameter('jsonrpc_server.jms.handlers')) { + return; + } + + foreach ($container->getParameter('jsonrpc_server.jms.handlers') as $handler => $emid) { $this->configureRelationHandler($container, $handler, $emid); } } diff --git a/DependencyInjection/BankiruJsonRpcServerExtension.php b/DependencyInjection/BankiruJsonRpcServerExtension.php index 4f8fe21..03c8e66 100644 --- a/DependencyInjection/BankiruJsonRpcServerExtension.php +++ b/DependencyInjection/BankiruJsonRpcServerExtension.php @@ -66,7 +66,11 @@ private function configureJmsAdapter(ContainerBuilder $container, array $config) $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config/adapters')); $loader->load('jms.yml'); - $container->setParameter('jsonrpc_server.handlers', (array)$config['adapters']['jms']['relation_handlers']); + if (!$this->isConfigEnabled($container, $config['adapters']['jms'])) { + return; + } + + $container->setParameter('jsonrpc_server.jms.handlers', (array)$config['adapters']['jms']['relation_handlers']); } /** diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 853c1c9..6496463 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -32,6 +32,8 @@ private function configureJms(ArrayNodeDefinition $adapters) { $jms = $adapters->children()->arrayNode('jms'); + $jms->canBeEnabled(); + $jms->children() ->arrayNode('relation_handlers') ->fixXmlConfig('relation_handler') diff --git a/Resources/config/adapters/jms.yml b/Resources/config/adapters/jms.yml index 188db89..0c31e20 100644 --- a/Resources/config/adapters/jms.yml +++ b/Resources/config/adapters/jms.yml @@ -1,3 +1,6 @@ +parameters: + jsonrpc_server.jms.handlers: [] + services: jsonrpc_server.jms_adapter.view_listener: class: Bankiru\Api\JsonRpc\Adapters\JMS\SerializedJsonRpcResponseListener From c078eafa89eb0054ee825a68b62dc3a787324f4b Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Fri, 28 Jul 2017 08:47:42 +0300 Subject: [PATCH 20/29] Allow JMS 2.0 bundle --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index bb26f79..9c4ada4 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "symfony/translation": "~2.7 || ~3.0 || ~4.0", "doctrine/doctrine-bundle": "~1.6", "doctrine/orm": "~2.3", - "jms/serializer-bundle": "~1.1", + "jms/serializer-bundle": "~1.5 || ~2.0", "bankiru/jsonrpc-browser-kit-client": "~1.0" }, "suggest": { From bb52a935ee3afc64b2679a36f978b270ea157bc3 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Fri, 28 Jul 2017 09:35:50 +0300 Subject: [PATCH 21/29] Fix test metadata --- Test/Resources/config/doctrine/SampleEntity.orm.yml | 5 ++++- Test/Resources/config/serializer/Entity.SampleEntity.yml | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Test/Resources/config/doctrine/SampleEntity.orm.yml b/Test/Resources/config/doctrine/SampleEntity.orm.yml index 5a6ef94..48d9977 100644 --- a/Test/Resources/config/doctrine/SampleEntity.orm.yml +++ b/Test/Resources/config/doctrine/SampleEntity.orm.yml @@ -1,7 +1,10 @@ Bankiru\Api\JsonRpc\Test\Entity\SampleEntity: type: entity id: - id: { type: integer } + id: + type: integer + generator: + strategy: AUTO fields: payload: {type: string} secret: {type: string} diff --git a/Test/Resources/config/serializer/Entity.SampleEntity.yml b/Test/Resources/config/serializer/Entity.SampleEntity.yml index 46c823d..bbfff37 100644 --- a/Test/Resources/config/serializer/Entity.SampleEntity.yml +++ b/Test/Resources/config/serializer/Entity.SampleEntity.yml @@ -1,6 +1,11 @@ Bankiru\Api\JsonRpc\Test\Entity\SampleEntity: exclusion_policy: ALL properties: + id: + type: integer + serialized_name: id + expose: true + payload: type: string serialized_name: sample_payload From 16c53f8c4554cde3ceb271cabe2d1161a171a98b Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Mon, 31 Jul 2017 13:52:24 +0300 Subject: [PATCH 22/29] Add symfony normalizer integration --- .travis.yml | 2 + Adapters/Builtin/BuiltinNormalizerAdapter.php | 18 ++++ Adapters/Builtin/JsonEncodeViewListener.php | 34 -------- Adapters/JMS/JmsNormalizerAdapter.php | 42 +++++++++ .../JMS/SerializedJsonRpcResponseListener.php | 86 ------------------- Adapters/Symfony/SymfonyNormalizerAdapter.php | 32 +++++++ BankiruJsonRpcServerBundle.php | 2 + .../BankiruJsonRpcServerExtension.php | 7 +- .../SymfonyAdapterConfigurationPass.php | 46 ++++++++++ DependencyInjection/Configuration.php | 14 ++- Listener/ViewListener.php | 54 ++++++++++++ NormalizerInterface.php | 16 ++++ Resources/config/adapters/builtin.yml | 4 +- Resources/config/adapters/jms.yml | 5 +- Resources/config/adapters/symfony.yml | 5 ++ Resources/config/jsonrpc.yml | 1 + Test/Kernel/TestKernel.php | 8 ++ Test/Kernel/config.yml | 2 + Test/Kernel/config_jms.yml | 3 +- Test/Kernel/config_symfony.yml | 6 ++ composer.json | 1 + 21 files changed, 257 insertions(+), 131 deletions(-) create mode 100644 Adapters/Builtin/BuiltinNormalizerAdapter.php delete mode 100644 Adapters/Builtin/JsonEncodeViewListener.php create mode 100644 Adapters/JMS/JmsNormalizerAdapter.php delete mode 100644 Adapters/JMS/SerializedJsonRpcResponseListener.php create mode 100644 Adapters/Symfony/SymfonyNormalizerAdapter.php create mode 100644 DependencyInjection/Compiler/SymfonyAdapterConfigurationPass.php create mode 100644 Listener/ViewListener.php create mode 100644 NormalizerInterface.php create mode 100644 Resources/config/adapters/symfony.yml create mode 100644 Test/Kernel/config_symfony.yml diff --git a/.travis.yml b/.travis.yml index 14781d3..a97ff9e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,4 +48,6 @@ script: - vendor/bin/phpunit --colors -c phpunit.xml --testsuite unit - vendor/bin/phpunit --colors -c phpunit.xml --testsuite integration - JMS_BUNDLE=1 vendor/bin/phpunit --colors -c phpunit.xml --testsuite integration + - SYMFONY_SERIALIZER=1 vendor/bin/phpunit --colors -c phpunit.xml --testsuite integration - JMS_BUNDLE=1 DOCTRINE_BUNDLE=1 vendor/bin/phpunit --colors -c phpunit.xml --testsuite integration + - SYMFONY_SERIALIZER=1 DOCTRINE_BUNDLE=1 vendor/bin/phpunit --colors -c phpunit.xml --testsuite integration diff --git a/Adapters/Builtin/BuiltinNormalizerAdapter.php b/Adapters/Builtin/BuiltinNormalizerAdapter.php new file mode 100644 index 0000000..123d430 --- /dev/null +++ b/Adapters/Builtin/BuiltinNormalizerAdapter.php @@ -0,0 +1,18 @@ +getRequest(); - $response = $event->getResponse(); - - // No need to perform JSON-RPC serialization here - if (!$request instanceof JsonRpcRequestInterface || $request->isNotification()) { - return; - } - - // Response is already properly formatted - if ($response instanceof JsonRpcResponseInterface) { - return; - } - - $event->setResponse( - new JsonRpcResponse( - $request->getId(), - $event->getResponse() - ) - ); - } -} diff --git a/Adapters/JMS/JmsNormalizerAdapter.php b/Adapters/JMS/JmsNormalizerAdapter.php new file mode 100644 index 0000000..c1ab862 --- /dev/null +++ b/Adapters/JMS/JmsNormalizerAdapter.php @@ -0,0 +1,42 @@ +normalizer = $normalizer; + $this->contextFactory = $contextFactory; + } + + /** {@inheritdoc} */ + public function normalize($entity, array $context = []) + { + if (null === $entity) { + return null; + } + + $jmsContext = $this->contextFactory->createSerializationContext(); + $jmsContext->setGroups($context); + + return $this->normalizer->toArray($entity, $jmsContext); + } +} diff --git a/Adapters/JMS/SerializedJsonRpcResponseListener.php b/Adapters/JMS/SerializedJsonRpcResponseListener.php deleted file mode 100644 index 25195b5..0000000 --- a/Adapters/JMS/SerializedJsonRpcResponseListener.php +++ /dev/null @@ -1,86 +0,0 @@ -serializer = $serializer; - } - - public function onPlainResponse(ViewEvent $event) - { - $request = $event->getRequest(); - $response = $event->getResponse(); - - // No need to perform JSON-RPC serialization here - if (!$request instanceof JsonRpcRequestInterface || $request->isNotification()) { - return; - } - - // Response is already properly formatted - if ($response instanceof JsonRpcResponseInterface) { - return; - } - - $response = new JsonRpcResponse( - $request->getId(), - $this->serializer->toArray( - $event->getResponse(), - $this->createSerializerContext($event) - - ) - ); - $event->setResponse($response); - } - - /** - * @param ViewEvent $event - * - * @return SerializationContext - * @throws \LogicException - */ - private function createSerializerContext(ViewEvent $event) - { - $context = SerializationContext::create(); - $context->setSerializeNull(true); - $attributes = $event->getRequest()->getAttributes(); - $defaults = $attributes->get('_with_default_context', true); - - if (!$defaults && (false === $attributes->get('_context', false))) { - throw new \LogicException( - 'Could not perform object serialization as no default context allowed and no custom set' - ); - } - - $groups = []; - if ($defaults) { - $groups[] = 'Default'; - } - if (false !== $attributes->get('_context', false)) { - foreach ((array)$attributes->get('_context') as $group) { - $groups[] = $group; - } - } - - $context->setGroups($groups); - - return $context; - } -} diff --git a/Adapters/Symfony/SymfonyNormalizerAdapter.php b/Adapters/Symfony/SymfonyNormalizerAdapter.php new file mode 100644 index 0000000..54b9738 --- /dev/null +++ b/Adapters/Symfony/SymfonyNormalizerAdapter.php @@ -0,0 +1,32 @@ +normalizer = $normalizer; + } + + /** {@inheritdoc} */ + public function normalize($entity, array $context = []) + { + if (null === $entity) { + return null; + } + + return $this->normalizer->normalize($entity, ['groups' => $context]); + } +} diff --git a/BankiruJsonRpcServerBundle.php b/BankiruJsonRpcServerBundle.php index e894d1b..c289549 100644 --- a/BankiruJsonRpcServerBundle.php +++ b/BankiruJsonRpcServerBundle.php @@ -4,6 +4,7 @@ use Bankiru\Api\JsonRpc\Adapters\JMS\Compiler\RelationHandlerPass; use Bankiru\Api\JsonRpc\DependencyInjection\BankiruJsonRpcServerExtension; +use Bankiru\Api\JsonRpc\DependencyInjection\Compiler\SymfonyAdapterConfigurationPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -14,6 +15,7 @@ final class BankiruJsonRpcServerBundle extends Bundle public function build(ContainerBuilder $container) { parent::build($container); + $container->addCompilerPass(new SymfonyAdapterConfigurationPass()); $container->addCompilerPass(new RelationHandlerPass()); } diff --git a/DependencyInjection/BankiruJsonRpcServerExtension.php b/DependencyInjection/BankiruJsonRpcServerExtension.php index 03c8e66..f308dc9 100644 --- a/DependencyInjection/BankiruJsonRpcServerExtension.php +++ b/DependencyInjection/BankiruJsonRpcServerExtension.php @@ -2,6 +2,7 @@ namespace Bankiru\Api\JsonRpc\DependencyInjection; +use Bankiru\Api\JsonRpc\Listener\ViewListener; use Bankiru\Api\Rpc\RpcEvents; use JMS\SerializerBundle\JMSSerializerBundle; use Symfony\Bundle\SecurityBundle\SecurityBundle; @@ -9,6 +10,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Reference; final class BankiruJsonRpcServerExtension extends Extension { @@ -24,9 +26,10 @@ public function load(array $configs, ContainerBuilder $container) $this->configureBuiltinAdapter($container); $this->configureJmsAdapter($container, $config); - if (!empty($config['view_listener'])) { - $container->getDefinition($config['view_listener']) + if ($this->isConfigEnabled($container, $config['view_listener'])) { + $container->register('jsonrpc_server.response_listener.normalizer', ViewListener::class) ->setPublic(true) + ->setArguments([new Reference($config['view_listener']['normalizer'])]) ->addTag( 'kernel.event_listener', [ diff --git a/DependencyInjection/Compiler/SymfonyAdapterConfigurationPass.php b/DependencyInjection/Compiler/SymfonyAdapterConfigurationPass.php new file mode 100644 index 0000000..def1419 --- /dev/null +++ b/DependencyInjection/Compiler/SymfonyAdapterConfigurationPass.php @@ -0,0 +1,46 @@ +hasDefinition('serializer')) { + return; + } + + if ($this->hasSymfonySerializer($container)) { + $this->configureSymfonyAdapter($container); + } + } + + /** + * @param ContainerBuilder $container + */ + private function configureSymfonyAdapter(ContainerBuilder $container) + { + $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../../Resources/config/adapters')); + + $loader->load('symfony.yml'); + } + + /** + * @param ContainerBuilder $container + * + * @return bool + */ + private function hasSymfonySerializer(ContainerBuilder $container) + { + $interfaces = class_implements($container->getDefinition('serializer')->getClass()); + + return in_array(NormalizerInterface::class, $interfaces, true); + } +} diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 6496463..d4ae6e8 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -14,10 +14,16 @@ public function getConfigTreeBuilder() $builder = new TreeBuilder(); $root = $builder->root('jsonrpc_server'); - $root->children() - ->scalarNode('view_listener') - ->info('View listener service ID') - ->defaultValue('jsonrpc_server.builtin_adapter.view_listener'); + $viewListener = $root + ->children() + ->arrayNode('view_listener') + ->canBeEnabled(); + + $viewListener + ->children() + ->scalarNode('normalizer') + ->info('View listener normalizer service ID') + ->defaultValue('jsonrpc_server.builtin_adapter.normalizer'); $adapters = $root->children()->arrayNode('adapters'); $this->configureJms($adapters); diff --git a/Listener/ViewListener.php b/Listener/ViewListener.php new file mode 100644 index 0000000..a1aa352 --- /dev/null +++ b/Listener/ViewListener.php @@ -0,0 +1,54 @@ +normalizer = $normalizer; + } + + public function onPlainResponse(ViewEvent $event) + { + $request = $event->getRequest(); + $response = $event->getResponse(); + + // No need to perform JSON-RPC serialization here + if (!$request instanceof RichJsonRpcRequest || $request->isNotification()) { + return; + } + + // Response is already properly formatted + if ($response instanceof JsonRpcResponseInterface) { + return; + } + + $response = new JsonRpcResponse( + $request->getId(), + $this->normalizer->normalize( + $event->getResponse(), + $request->getAttributes()->get('_context') + ) + ); + + $event->setResponse($response); + } + +} diff --git a/NormalizerInterface.php b/NormalizerInterface.php new file mode 100644 index 0000000..4ed0fd0 --- /dev/null +++ b/NormalizerInterface.php @@ -0,0 +1,16 @@ +load(__DIR__ . '/config_jms.yml'); } + if (false !== getenv('SYMFONY_SERIALIZER')) { + $loader->load(__DIR__ . '/config_symfony.yml'); + } + if (false !== getenv('DOCTRINE_BUNDLE')) { $loader->load(__DIR__ . '/config_doctrine.yml'); } @@ -68,6 +72,10 @@ public function getName() $name .= '_jms'; } + if (false !== getenv('SYMFONY_SERIALIZER')) { + $name .= '_symfony'; + } + if (false !== getenv('DOCTRINE_BUNDLE')) { $name .= '_doctrine'; } diff --git a/Test/Kernel/config.yml b/Test/Kernel/config.yml index 1767601..223aeb0 100644 --- a/Test/Kernel/config.yml +++ b/Test/Kernel/config.yml @@ -21,6 +21,8 @@ security: - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY } jsonrpc_server: + view_listener: + enabled: true adapters: jms: relation_handlers: diff --git a/Test/Kernel/config_jms.yml b/Test/Kernel/config_jms.yml index 211a1a1..e766955 100644 --- a/Test/Kernel/config_jms.yml +++ b/Test/Kernel/config_jms.yml @@ -6,4 +6,5 @@ jms_serializer: auto_detection: true jsonrpc_server: - view_listener: jsonrpc_server.jms_adapter.view_listener + view_listener: + normalizer: jsonrpc_server.jms_adapter.normalizer diff --git a/Test/Kernel/config_symfony.yml b/Test/Kernel/config_symfony.yml new file mode 100644 index 0000000..1d260a1 --- /dev/null +++ b/Test/Kernel/config_symfony.yml @@ -0,0 +1,6 @@ +framework: + serializer: ~ + +jsonrpc_server: + view_listener: + normalizer: jsonrpc_server.symfony_adapter.normalizer diff --git a/composer.json b/composer.json index 9c4ada4..152e456 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ "symfony/security-bundle": "~2.7 || ~3.0 || ~4.0", "symfony/browser-kit": "~2.7 || ~3.0 || ~4.0", "symfony/translation": "~2.7 || ~3.0 || ~4.0", + "symfony/serializer": "~2.7 || ~3.0 || ~4.0", "doctrine/doctrine-bundle": "~1.6", "doctrine/orm": "~2.3", "jms/serializer-bundle": "~1.5 || ~2.0", From 74876016ef1f66a74520296375a5f37ce2b4b640 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Tue, 1 Aug 2017 09:28:26 +0300 Subject: [PATCH 23/29] Fix listener identifier --- Resources/config/jsonrpc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/config/jsonrpc.yml b/Resources/config/jsonrpc.yml index 24f704c..bf4e535 100644 --- a/Resources/config/jsonrpc.yml +++ b/Resources/config/jsonrpc.yml @@ -20,7 +20,7 @@ services: - { name: kernel.event_listener, event: rpc.response, method: filterNotificationResponse, priority: -255 } - jsonrpc_server.reponse_listener.match_id: + jsonrpc_server.response_listener.match_id: class: Bankiru\Api\JsonRpc\Listener\IdMatcherListener tags: - { name: kernel.event_listener, event: rpc.response, method: onFilterResponse, priority: -254 } From 71537983c2462ff070f3b24775b47ba394bffadc Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Tue, 1 Aug 2017 11:35:01 +0300 Subject: [PATCH 24/29] allow empty adapters configuration --- DependencyInjection/Configuration.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index d4ae6e8..4e29aa7 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -17,7 +17,7 @@ public function getConfigTreeBuilder() $viewListener = $root ->children() ->arrayNode('view_listener') - ->canBeEnabled(); + ->canBeDisabled(); $viewListener ->children() @@ -25,7 +25,7 @@ public function getConfigTreeBuilder() ->info('View listener normalizer service ID') ->defaultValue('jsonrpc_server.builtin_adapter.normalizer'); - $adapters = $root->children()->arrayNode('adapters'); + $adapters = $root->children()->arrayNode('adapters')->addDefaultsIfNotSet(); $this->configureJms($adapters); return $builder; From e61797f6d2e4edd64b000fe7f1ce5b0e34004e46 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Wed, 2 Aug 2017 14:12:44 +0300 Subject: [PATCH 25/29] Allow scalars not to be normalized --- Listener/ViewListener.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Listener/ViewListener.php b/Listener/ViewListener.php index a1aa352..2a0a689 100644 --- a/Listener/ViewListener.php +++ b/Listener/ViewListener.php @@ -40,13 +40,12 @@ public function onPlainResponse(ViewEvent $event) return; } - $response = new JsonRpcResponse( - $request->getId(), - $this->normalizer->normalize( - $event->getResponse(), - $request->getAttributes()->get('_context') - ) - ); + $content = $event->getResponse(); + if (!is_scalar($content) && null !== $content) { + $content = $this->normalizer->normalize($content, $request->getAttributes()->get('_context')); + } + + $response = new JsonRpcResponse($request->getId(), $content); $event->setResponse($response); } From 7eea4fe32959a9fc6cced901d2463b442e96a12c Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Wed, 2 Aug 2017 14:59:08 +0300 Subject: [PATCH 26/29] Allow definition class to be a parameter for 2.x --- .../Compiler/SymfonyAdapterConfigurationPass.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DependencyInjection/Compiler/SymfonyAdapterConfigurationPass.php b/DependencyInjection/Compiler/SymfonyAdapterConfigurationPass.php index def1419..0486e51 100644 --- a/DependencyInjection/Compiler/SymfonyAdapterConfigurationPass.php +++ b/DependencyInjection/Compiler/SymfonyAdapterConfigurationPass.php @@ -39,7 +39,9 @@ private function configureSymfonyAdapter(ContainerBuilder $container) */ private function hasSymfonySerializer(ContainerBuilder $container) { - $interfaces = class_implements($container->getDefinition('serializer')->getClass()); + $interfaces = class_implements( + $container->getParameterBag()->resolveValue($container->getDefinition('serializer')->getClass()) + ); return in_array(NormalizerInterface::class, $interfaces, true); } From a31d32e78ce5c2a7ae1bb0e28aec677ddeb1da48 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Thu, 10 Aug 2017 15:58:09 +0300 Subject: [PATCH 27/29] Split normalizer and view listener --- .../BankiruJsonRpcServerExtension.php | 10 ++-- DependencyInjection/Configuration.php | 6 +-- Listener/NormalizingListener.php | 50 +++++++++++++++++++ Listener/ViewListener.php | 24 +-------- README.md | 4 +- Resources/config/jsonrpc.yml | 4 ++ Test/Kernel/config.yml | 2 +- Test/Kernel/config_jms.yml | 2 +- Test/Kernel/config_symfony.yml | 2 +- 9 files changed, 68 insertions(+), 36 deletions(-) create mode 100644 Listener/NormalizingListener.php diff --git a/DependencyInjection/BankiruJsonRpcServerExtension.php b/DependencyInjection/BankiruJsonRpcServerExtension.php index f308dc9..254438a 100644 --- a/DependencyInjection/BankiruJsonRpcServerExtension.php +++ b/DependencyInjection/BankiruJsonRpcServerExtension.php @@ -2,7 +2,7 @@ namespace Bankiru\Api\JsonRpc\DependencyInjection; -use Bankiru\Api\JsonRpc\Listener\ViewListener; +use Bankiru\Api\JsonRpc\Listener\NormalizingListener; use Bankiru\Api\Rpc\RpcEvents; use JMS\SerializerBundle\JMSSerializerBundle; use Symfony\Bundle\SecurityBundle\SecurityBundle; @@ -26,16 +26,16 @@ public function load(array $configs, ContainerBuilder $container) $this->configureBuiltinAdapter($container); $this->configureJmsAdapter($container, $config); - if ($this->isConfigEnabled($container, $config['view_listener'])) { - $container->register('jsonrpc_server.response_listener.normalizer', ViewListener::class) + if ($this->isConfigEnabled($container, $config['normalizer_listener'])) { + $container->register('jsonrpc_server.response_listener.normalizer', NormalizingListener::class) ->setPublic(true) - ->setArguments([new Reference($config['view_listener']['normalizer'])]) + ->setArguments([new Reference($config['normalizer_listener']['normalizer'])]) ->addTag( 'kernel.event_listener', [ 'event' => RpcEvents::VIEW, 'method' => 'onPlainResponse', - 'priority' => -254, + 'priority' => 255, ] ); } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 4e29aa7..7a0a878 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -14,12 +14,12 @@ public function getConfigTreeBuilder() $builder = new TreeBuilder(); $root = $builder->root('jsonrpc_server'); - $viewListener = $root + $normListener = $root ->children() - ->arrayNode('view_listener') + ->arrayNode('normalizer_listener') ->canBeDisabled(); - $viewListener + $normListener ->children() ->scalarNode('normalizer') ->info('View listener normalizer service ID') diff --git a/Listener/NormalizingListener.php b/Listener/NormalizingListener.php new file mode 100644 index 0000000..8ef1328 --- /dev/null +++ b/Listener/NormalizingListener.php @@ -0,0 +1,50 @@ +normalizer = $normalizer; + } + + public function onPlainResponse(ViewEvent $event) + { + $request = $event->getRequest(); + $response = $event->getResponse(); + + // No need to perform JSON-RPC serialization here + if (!$request instanceof RichJsonRpcRequest || $request->isNotification()) { + return; + } + + // Response is already properly formatted + if ($response instanceof JsonRpcResponseInterface) { + return; + } + + $content = $event->getResponse(); + if (!is_scalar($content) && null !== $content) { + $content = $this->normalizer->normalize($content, $request->getAttributes()->get('_context')); + } + + $event->setResponse($content); + } + +} diff --git a/Listener/ViewListener.php b/Listener/ViewListener.php index 2a0a689..1c951bc 100644 --- a/Listener/ViewListener.php +++ b/Listener/ViewListener.php @@ -2,7 +2,6 @@ namespace Bankiru\Api\JsonRpc\Listener; -use Bankiru\Api\JsonRpc\NormalizerInterface; use Bankiru\Api\JsonRpc\Specification\JsonRpcResponse; use Bankiru\Api\JsonRpc\Specification\RichJsonRpcRequest; use Bankiru\Api\Rpc\Event\ViewEvent; @@ -10,21 +9,6 @@ final class ViewListener { - /** - * @var NormalizerInterface - */ - private $normalizer; - - /** - * ViewListener constructor. - * - * @param NormalizerInterface $normalizer - */ - public function __construct(NormalizerInterface $normalizer) - { - $this->normalizer = $normalizer; - } - public function onPlainResponse(ViewEvent $event) { $request = $event->getRequest(); @@ -40,14 +24,8 @@ public function onPlainResponse(ViewEvent $event) return; } - $content = $event->getResponse(); - if (!is_scalar($content) && null !== $content) { - $content = $this->normalizer->normalize($content, $request->getAttributes()->get('_context')); - } - - $response = new JsonRpcResponse($request->getId(), $content); + $response = new JsonRpcResponse($request->getId(), $response); $event->setResponse($response); } - } diff --git a/README.md b/README.md index 71a3e3b..fd0ee9c 100644 --- a/README.md +++ b/README.md @@ -64,8 +64,8 @@ Configuration reference: ```yaml jsonrpc_server: - # View listener service ID - view_listener: jsonrpc_server.builtin_adapter.view_listener + # Normalizer listener service ID + normalizer_listener: jsonrpc_server.builtin_adapter.view_listener adapters: jms: relation_handlers: diff --git a/Resources/config/jsonrpc.yml b/Resources/config/jsonrpc.yml index bf4e535..76511f4 100644 --- a/Resources/config/jsonrpc.yml +++ b/Resources/config/jsonrpc.yml @@ -19,6 +19,10 @@ services: tags: - { name: kernel.event_listener, event: rpc.response, method: filterNotificationResponse, priority: -255 } + jsonrpc_server.plain_view_listener: + class: Bankiru\Api\JsonRpc\Listener\ViewListener + tags: + - { name: kernel.event_listener, event: rpc.view, method: onPlainResponse, priority: -255 } jsonrpc_server.response_listener.match_id: class: Bankiru\Api\JsonRpc\Listener\IdMatcherListener diff --git a/Test/Kernel/config.yml b/Test/Kernel/config.yml index 223aeb0..d0ce868 100644 --- a/Test/Kernel/config.yml +++ b/Test/Kernel/config.yml @@ -21,7 +21,7 @@ security: - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY } jsonrpc_server: - view_listener: + normalizer_listener: enabled: true adapters: jms: diff --git a/Test/Kernel/config_jms.yml b/Test/Kernel/config_jms.yml index e766955..fa7a916 100644 --- a/Test/Kernel/config_jms.yml +++ b/Test/Kernel/config_jms.yml @@ -6,5 +6,5 @@ jms_serializer: auto_detection: true jsonrpc_server: - view_listener: + normalizer_listener: normalizer: jsonrpc_server.jms_adapter.normalizer diff --git a/Test/Kernel/config_symfony.yml b/Test/Kernel/config_symfony.yml index 1d260a1..440a8b3 100644 --- a/Test/Kernel/config_symfony.yml +++ b/Test/Kernel/config_symfony.yml @@ -2,5 +2,5 @@ framework: serializer: ~ jsonrpc_server: - view_listener: + normalizer_listener: normalizer: jsonrpc_server.symfony_adapter.normalizer From dc275bbfe71c23511f314ce8fd8021f306c2a9d8 Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Thu, 10 Aug 2017 16:09:37 +0300 Subject: [PATCH 28/29] fix cs --- Listener/NormalizingListener.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Listener/NormalizingListener.php b/Listener/NormalizingListener.php index 8ef1328..94d1dba 100644 --- a/Listener/NormalizingListener.php +++ b/Listener/NormalizingListener.php @@ -46,5 +46,4 @@ public function onPlainResponse(ViewEvent $event) $event->setResponse($content); } - } From 94fc31d9b949a08407c5f8f722f1a5a59528228a Mon Sep 17 00:00:00 2001 From: Pavel Batanov Date: Mon, 14 Aug 2017 12:38:50 +0300 Subject: [PATCH 29/29] Allow common ObjectManager to be used with handler --- Adapters/JMS/RelationsHandler.php | 36 +++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/Adapters/JMS/RelationsHandler.php b/Adapters/JMS/RelationsHandler.php index 8ec9add..0073623 100644 --- a/Adapters/JMS/RelationsHandler.php +++ b/Adapters/JMS/RelationsHandler.php @@ -2,30 +2,29 @@ namespace Bankiru\Api\JsonRpc\Adapters\JMS; -use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Common\Persistence\ObjectManager; use Doctrine\ORM\Mapping\ClassMetadata; -use JMS\Serializer\Context; use JMS\Serializer\JsonDeserializationVisitor; use JMS\Serializer\JsonSerializationVisitor; final class RelationsHandler { /** - * @var EntityManagerInterface + * @var ObjectManager */ private $manager; /** * RelationsHandler constructor. * - * @param EntityManagerInterface $manager + * @param ObjectManager $manager */ - public function __construct(EntityManagerInterface $manager) + public function __construct(ObjectManager $manager) { $this->manager = $manager; } - public function serializeRelation(JsonSerializationVisitor $visitor, $relation, array $type, Context $context) + public function serializeRelation(JsonSerializationVisitor $visitor, $relation) { if ($relation instanceof \Traversable) { $relation = iterator_to_array($relation); @@ -38,7 +37,7 @@ public function serializeRelation(JsonSerializationVisitor $visitor, $relation, return $this->getSingleEntityRelation($relation); } - public function deserializeRelation(JsonDeserializationVisitor $visitor, $relation, array $type, Context $context) + public function deserializeRelation(JsonDeserializationVisitor $visitor, $relation, array $type) { $className = isset($type['params'][0]['name']) ? $type['params'][0]['name'] : null; @@ -50,7 +49,7 @@ public function deserializeRelation(JsonDeserializationVisitor $visitor, $relati $metadata = $this->manager->getClassMetadata($className); if (!is_array($relation)) { - return $this->manager->getReference($className, $relation); + return $this->deserializeIdentifier($className, $relation); } $single = false; @@ -62,12 +61,12 @@ public function deserializeRelation(JsonDeserializationVisitor $visitor, $relati } if ($single) { - return $this->manager->getReference($className, $relation); + return $this->deserializeIdentifier($className, $relation); } $objects = []; foreach ($relation as $idSet) { - $objects[] = $this->manager->getReference($className, $idSet); + $objects[] = $this->deserializeIdentifier($className, $idSet); } return $objects; @@ -83,10 +82,25 @@ private function getSingleEntityRelation($relation) $metadata = $this->manager->getClassMetadata(get_class($relation)); $ids = $metadata->getIdentifierValues($relation); - if (!$metadata->isIdentifierComposite) { + if (1 === count($metadata->getIdentifierFieldNames())) { $ids = array_shift($ids); } return $ids; } + + /** + * @param string $className + * @param mixed $identifier + * + * @return object + */ + private function deserializeIdentifier($className, $identifier) + { + if (method_exists($this->manager, 'getReference')) { + return $this->manager->getReference($className, $identifier); + } + + return $this->manager->find($className, $identifier); + } }