From ef282e72acfb456d5da1ec1cadde93b48b381b2f Mon Sep 17 00:00:00 2001 From: Yoanm Date: Fri, 31 Dec 2021 09:24:34 +0100 Subject: [PATCH 1/2] Request listener instead of controller --- features/demo_app/default_config/routes.yaml | 3 - .../mapping_collector_config/routes.yaml | 3 - src/EventListener/RequestListener.php | 76 +++++++++++++++++++ src/Resources/config/routing/endpoint.xml | 21 ----- src/Resources/config/services.public.yaml | 8 ++ 5 files changed, 84 insertions(+), 27 deletions(-) delete mode 100644 features/demo_app/default_config/routes.yaml delete mode 100644 features/demo_app/mapping_collector_config/routes.yaml create mode 100644 src/EventListener/RequestListener.php delete mode 100644 src/Resources/config/routing/endpoint.xml diff --git a/features/demo_app/default_config/routes.yaml b/features/demo_app/default_config/routes.yaml deleted file mode 100644 index d9f4860..0000000 --- a/features/demo_app/default_config/routes.yaml +++ /dev/null @@ -1,3 +0,0 @@ -# Import default configuration or write your own -json-rpc-endpoint: - resource: '@JsonRpcHttpServerBundle/Resources/config/routing/endpoint.xml' diff --git a/features/demo_app/mapping_collector_config/routes.yaml b/features/demo_app/mapping_collector_config/routes.yaml deleted file mode 100644 index d9f4860..0000000 --- a/features/demo_app/mapping_collector_config/routes.yaml +++ /dev/null @@ -1,3 +0,0 @@ -# Import default configuration or write your own -json-rpc-endpoint: - resource: '@JsonRpcHttpServerBundle/Resources/config/routing/endpoint.xml' diff --git a/src/EventListener/RequestListener.php b/src/EventListener/RequestListener.php new file mode 100644 index 0000000..53faef8 --- /dev/null +++ b/src/EventListener/RequestListener.php @@ -0,0 +1,76 @@ +uri = $uri; + $this->sdkEndpoint = $sdkEndpoint; + $this->allowedMethodList = [Request::METHOD_POST, Request::METHOD_OPTIONS]; + } + + public function onKernelRequest(RequestEvent $event) + { + if (!$event->isMasterRequest()) { + // Don't do anything if it's not the master request ! + return; + } + + $request = $event->getRequest(); + if ($this->uri === $request->getRequestUri()) { + switch ($request->getMethod()) { + case Request::METHOD_POST: + $event->setResponse($this->httpPost($request)); + break; + case Request::METHOD_OPTIONS: + $event->setResponse($this->httpOptions()); + break; + } + } + } + + protected function httpOptions() : Response + { + $response = new Response(); + $response->headers->set('Content-Type', 'application/json'); + + // Set allowed http methods + $allowedMethodListString = implode(', ', $this->allowedMethodList); + $response->headers->set('Allow', $allowedMethodListString); + $response->headers->set('Access-Control-Request-Method', $allowedMethodListString); + + // Set allowed content type + $response->headers->set('Accept', 'application/json'); + $response->headers->set('Access-Control-Allow-Headers', 'Content-Type'); + + return $response; + } + + protected function httpPost(Request $request) : Response + { + $response = new Response(); + $response->headers->set('Content-Type', 'application/json'); + + $response->setContent( + $this->sdkEndpoint->index( + $request->getContent() + ) + ); + + return $response; + } +} diff --git a/src/Resources/config/routing/endpoint.xml b/src/Resources/config/routing/endpoint.xml deleted file mode 100644 index ec90f84..0000000 --- a/src/Resources/config/routing/endpoint.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - json_rpc_http_server.endpoint::httpPost - - - json_rpc_http_server.endpoint::httpOptions - - diff --git a/src/Resources/config/services.public.yaml b/src/Resources/config/services.public.yaml index 468d994..d73c5ec 100644 --- a/src/Resources/config/services.public.yaml +++ b/src/Resources/config/services.public.yaml @@ -7,6 +7,14 @@ services: arguments: - '@json_rpc_server_sdk.infra.endpoint' + json_rpc_http_server.request_listener: + class: Yoanm\SymfonyJsonRpcHttpServer\EventListener\RequestListener + arguments: + - '@json_rpc_server_sdk.infra.endpoint' + - '%json_rpc_http_server.http_endpoint_path%' + tags: + - { name: kernel.event_listener, event: kernel.request, method: 'onKernelRequest', priority: 9999 } + json_rpc_http_server.dispatcher.server: class: Yoanm\SymfonyJsonRpcHttpServer\Dispatcher\SymfonyJsonRpcServerDispatcher arguments: From a9ae57694f3a00991ba734e2ad65a7ec7a285daa Mon Sep 17 00:00:00 2001 From: Yoanm Date: Fri, 31 Dec 2021 09:49:39 +0100 Subject: [PATCH 2/2] Add UT, remove useless --- src/Endpoint/JsonRpcHttpEndpoint.php | 66 ------ src/EventListener/RequestListener.php | 1 - src/Resources/config/services.private.yaml | 9 + src/Resources/config/services.public.yaml | 13 -- .../DependencyInjection/AbstractTestClass.php | 6 +- .../DependencyInjection/ConfigFilesTest.php | 16 +- .../JsonRpcHttpServerExtensionTest.php | 12 +- .../Endpoint/JsonRpcHttpEndpointTest.php | 74 ------- .../EventListener/RequestListenerTest.php | 201 ++++++++++++++++++ 9 files changed, 228 insertions(+), 170 deletions(-) delete mode 100644 src/Endpoint/JsonRpcHttpEndpoint.php delete mode 100644 tests/Functional/Endpoint/JsonRpcHttpEndpointTest.php create mode 100644 tests/Functional/EventListener/RequestListenerTest.php diff --git a/src/Endpoint/JsonRpcHttpEndpoint.php b/src/Endpoint/JsonRpcHttpEndpoint.php deleted file mode 100644 index 3aae8a3..0000000 --- a/src/Endpoint/JsonRpcHttpEndpoint.php +++ /dev/null @@ -1,66 +0,0 @@ -sdkEndpoint = $sdkEndpoint; - $this->allowedMethodList = [Request::METHOD_POST, Request::METHOD_OPTIONS]; - } - - /** - * @return Response - */ - public function httpOptions() : Response - { - $response = new Response(); - $response->headers->set('Content-Type', 'application/json'); - - // Set allowed http methods - $allowedMethodListString = implode(', ', $this->allowedMethodList); - $response->headers->set('Allow', $allowedMethodListString); - $response->headers->set('Access-Control-Request-Method', $allowedMethodListString); - - // Set allowed content type - $response->headers->set('Accept', 'application/json'); - $response->headers->set('Access-Control-Allow-Headers', 'Content-Type'); - - return $response; - } - - /** - * @param Request $request - * - * @return Response - */ - public function httpPost(Request $request) : Response - { - $response = new Response(); - $response->headers->set('Content-Type', 'application/json'); - - $response->setContent( - $this->sdkEndpoint->index( - $request->getContent() - ) - ); - - return $response; - } -} diff --git a/src/EventListener/RequestListener.php b/src/EventListener/RequestListener.php index 53faef8..956022e 100644 --- a/src/EventListener/RequestListener.php +++ b/src/EventListener/RequestListener.php @@ -17,7 +17,6 @@ class RequestListener public function __construct(SDKJsonRpcEndpoint $sdkEndpoint, $uri) { - var_dump("CONSTRUCT"); $this->uri = $uri; $this->sdkEndpoint = $sdkEndpoint; $this->allowedMethodList = [Request::METHOD_POST, Request::METHOD_OPTIONS]; diff --git a/src/Resources/config/services.private.yaml b/src/Resources/config/services.private.yaml index 70b2928..26d953f 100644 --- a/src/Resources/config/services.private.yaml +++ b/src/Resources/config/services.private.yaml @@ -11,3 +11,12 @@ services: # Alias method resolver (used in sdk.services.app.yml) json_rpc_http_server.alias.method_resolver: '@json_rpc_http_server.method_resolver' + json_rpc_http_server.request_listener: + class: Yoanm\SymfonyJsonRpcHttpServer\EventListener\RequestListener + arguments: + - '@json_rpc_server_sdk.infra.endpoint' + - '%json_rpc_http_server.http_endpoint_path%' + tags: + - { name: kernel.event_listener, event: kernel.request, method: 'onKernelRequest', priority: 9999 } + + diff --git a/src/Resources/config/services.public.yaml b/src/Resources/config/services.public.yaml index d73c5ec..bd8ed0a 100644 --- a/src/Resources/config/services.public.yaml +++ b/src/Resources/config/services.public.yaml @@ -2,19 +2,6 @@ services: _defaults: public: true - json_rpc_http_server.endpoint: - class: Yoanm\SymfonyJsonRpcHttpServer\Endpoint\JsonRpcHttpEndpoint - arguments: - - '@json_rpc_server_sdk.infra.endpoint' - - json_rpc_http_server.request_listener: - class: Yoanm\SymfonyJsonRpcHttpServer\EventListener\RequestListener - arguments: - - '@json_rpc_server_sdk.infra.endpoint' - - '%json_rpc_http_server.http_endpoint_path%' - tags: - - { name: kernel.event_listener, event: kernel.request, method: 'onKernelRequest', priority: 9999 } - json_rpc_http_server.dispatcher.server: class: Yoanm\SymfonyJsonRpcHttpServer\Dispatcher\SymfonyJsonRpcServerDispatcher arguments: diff --git a/tests/Common/DependencyInjection/AbstractTestClass.php b/tests/Common/DependencyInjection/AbstractTestClass.php index 7ae993f..d93919b 100644 --- a/tests/Common/DependencyInjection/AbstractTestClass.php +++ b/tests/Common/DependencyInjection/AbstractTestClass.php @@ -11,7 +11,7 @@ abstract class AbstractTestClass extends AbstractExtensionTestCase { - const EXPECTED_ENDPOINT_SERVICE_ID = 'json_rpc_http_server.endpoint'; + const EXPECTED_DISPATCHER_SERVICE_ID = 'json_rpc_http_server.dispatcher.server'; const EXPECTED_HTTP_ENDPOINT_PATH_CONTAINER_PARAM = 'json_rpc_http_server.http_endpoint_path'; const EXPECTED_PARAMS_VALIDATOR_ALIAS = 'json_rpc_http_server.alias.params_validator'; const EXPECTED_REQUEST_HANDLER_SERVICE_ID = 'json_rpc_server_sdk.app.handler.jsonrpc_request'; @@ -47,11 +47,11 @@ protected function loadContainer(array $configurationValues = [], $mockResolver } - protected function assertEndpointIsUsable() + protected function assertDispatcherInstalled() { // Retrieving this service will imply to load all related dependencies // Any binding issues will be raised - $this->assertNotNull($this->container->get(self::EXPECTED_ENDPOINT_SERVICE_ID)); + $this->assertNotNull($this->container->get(self::EXPECTED_DISPATCHER_SERVICE_ID)); } /** diff --git a/tests/Functional/DependencyInjection/ConfigFilesTest.php b/tests/Functional/DependencyInjection/ConfigFilesTest.php index 5615711..70e0d07 100644 --- a/tests/Functional/DependencyInjection/ConfigFilesTest.php +++ b/tests/Functional/DependencyInjection/ConfigFilesTest.php @@ -15,6 +15,7 @@ use Yoanm\SymfonyJsonRpcHttpServer\DependencyInjection\JsonRpcHttpServerExtension; use Yoanm\SymfonyJsonRpcHttpServer\Dispatcher\SymfonyJsonRpcServerDispatcher; use Yoanm\SymfonyJsonRpcHttpServer\Endpoint\JsonRpcHttpEndpoint; +use Yoanm\SymfonyJsonRpcHttpServer\EventListener\RequestListener; use Yoanm\SymfonyJsonRpcHttpServer\Resolver\MethodResolver; /** @@ -37,6 +38,7 @@ protected function getContainerExtensions(): array * @dataProvider provideSDKAppServiceIdAndClass * @dataProvider provideSDKInfraServiceIdAndClass * @dataProvider provideBundlePublicServiceIdAndClass + * @dataProvider provideBundlePrivateServiceIdAndClass * * @param string $serviceId * @param string $expectedClassName @@ -122,11 +124,6 @@ public function provideSDKInfraServiceIdAndClass() public function provideBundlePublicServiceIdAndClass() { return [ - 'Bundle - Public - HTTP endpoint' => [ - 'serviceId' => 'json_rpc_http_server.endpoint', - 'serviceClassName' => JsonRpcHttpEndpoint::class, - 'public' => true, - ], 'Bundle - Public - Event Dispatcher' => [ 'serviceId' => 'json_rpc_http_server.dispatcher.server', 'serviceClassName' => SymfonyJsonRpcServerDispatcher::class, @@ -141,15 +138,20 @@ public function provideBundlePublicServiceIdAndClass() public function provideBundlePrivateServiceIdAndClass() { return [ + 'Bundle - Public - HTTP endpoint' => [ + 'serviceId' => 'json_rpc_http_server.request_listener', + 'serviceClassName' => RequestListener::class, + 'public' => false, + ], 'Bundle - Private - JSON-RPC method resolver ServiceLocator' => [ 'serviceId' => 'json_rpc_http_server.service_locator.method_resolver', 'serviceClassName' => ServiceLocator::class, - 'public' => true, + 'public' => false, ], 'Bundle - Private - MethodResolver alias' => [ 'serviceId' => 'json_rpc_http_server.alias.method_resolver', 'serviceClassName' => MethodResolver::class, - 'public' => true, + 'public' => false, ], ]; } diff --git a/tests/Functional/DependencyInjection/JsonRpcHttpServerExtensionTest.php b/tests/Functional/DependencyInjection/JsonRpcHttpServerExtensionTest.php index 0d04c79..75a198d 100644 --- a/tests/Functional/DependencyInjection/JsonRpcHttpServerExtensionTest.php +++ b/tests/Functional/DependencyInjection/JsonRpcHttpServerExtensionTest.php @@ -31,7 +31,7 @@ public function testShouldBeLoadable() { $this->loadContainer(); - $this->assertEndpointIsUsable(); + $this->assertDispatcherInstalled(); } public function testShouldManageCustomEndpointPathFromConfiguration() @@ -42,7 +42,7 @@ public function testShouldManageCustomEndpointPathFromConfiguration() // Assert custom resolver is an alias of the stub $this->assertContainerBuilderHasParameter(self::EXPECTED_HTTP_ENDPOINT_PATH_CONTAINER_PARAM, $myCustomEndpoint); - $this->assertEndpointIsUsable(); + $this->assertDispatcherInstalled(); } public function testShouldReturnAnXsdValidationBasePath() @@ -67,7 +67,7 @@ public function testShouldBindServerDispatcherToDispatcherAwareService() 0 ); - $this->assertEndpointIsUsable(); + $this->assertDispatcherInstalled(); } public function testShouldThrowAnExceptionIfDispatcherAwareServiceDoesNotUseRightTrait() @@ -104,7 +104,7 @@ public function testShouldInjectParamsValidatorAliasIfDefined() [new Reference(self::EXPECTED_PARAMS_VALIDATOR_ALIAS)] ); - $this->assertEndpointIsUsable(); + $this->assertDispatcherInstalled(); } public function testShouldNotInjectParamsValidatorAliasIfNotDefined() @@ -118,7 +118,7 @@ public function testShouldNotInjectParamsValidatorAliasIfNotDefined() } } - $this->assertEndpointIsUsable(); + $this->assertDispatcherInstalled(); } public function testShouldBindJsonRpcMethodsToMethodAwareServices() @@ -165,7 +165,7 @@ public function testShouldBindJsonRpcMethodsToMethodAwareServices() 1 ); - $this->assertEndpointIsUsable(); + $this->assertDispatcherInstalled(); } public function testShouldThowAnExceptionIfMethodAwareServiceDoesNotImplementRightInterface() diff --git a/tests/Functional/Endpoint/JsonRpcHttpEndpointTest.php b/tests/Functional/Endpoint/JsonRpcHttpEndpointTest.php deleted file mode 100644 index 6ccddf6..0000000 --- a/tests/Functional/Endpoint/JsonRpcHttpEndpointTest.php +++ /dev/null @@ -1,74 +0,0 @@ -sdkEndpoint = $this->prophesize(SdkJsonRpcEndpoint::class); - - $this->endpoint = new JsonRpcHttpEndpoint( - $this->sdkEndpoint->reveal() - ); - } - - public function testHttPostShouldHandleRequestContentAndReturnA200ResponseContainingSDKEndpointReturnedValue() - { - $requestContent = 'request-content'; - $expectedResponseContent = 'expected-response-content'; - - /** @var Request|ObjectProphecy $request */ - $request = $this->prophesize(Request::class); - - $request->getContent() - ->willReturn($requestContent) - ->shouldBeCalled(); - - $this->sdkEndpoint->index($requestContent) - ->willReturn($expectedResponseContent) - ->shouldBeCalled(); - - $response = $this->endpoint->httpPost($request->reveal()); - - $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); - $this->assertSame($expectedResponseContent, $response->getContent()); - $this->assertSame('application/json', $response->headers->get('Content-Type')); - } - - public function testHttOptionsShouldReturnAllowedMethodsAndContentType() - { - $expectedAllowedMethodList = implode(', ', [Request::METHOD_POST, Request::METHOD_OPTIONS]); - - $response = $this->endpoint->httpOptions(); - - $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); - $this->assertSame('application/json', $response->headers->get('Content-Type')); - - // Check allowed methods - $this->assertSame($expectedAllowedMethodList, $response->headers->get('Allow', null)); - $this->assertSame( - $expectedAllowedMethodList, - $response->headers->get('Access-Control-Request-Method', null) - ); - - // Check allowed content types - $this->assertSame('application/json', $response->headers->get('Accept')); - $this->assertSame('Content-Type', $response->headers->get('Access-Control-Allow-Headers')); - } -} diff --git a/tests/Functional/EventListener/RequestListenerTest.php b/tests/Functional/EventListener/RequestListenerTest.php new file mode 100644 index 0000000..b9c6b4a --- /dev/null +++ b/tests/Functional/EventListener/RequestListenerTest.php @@ -0,0 +1,201 @@ +sdkEndpoint = $this->prophesize(SdkJsonRpcEndpoint::class); + + $this->endpoint = new RequestListener( + $this->sdkEndpoint->reveal(), + $this->uri + ); + } + + public function testHttPostShouldHandleRequestContentAndReturnA200ResponseContainingSDKEndpointReturnedValue() + { + $requestContent = 'request-content'; + $expectedResponseContent = 'expected-response-content'; + /** @var null|Request $actualResponse */ + $actualResponse = null; + + /** @var Request|ObjectProphecy $request */ + $request = $this->prophesize(Request::class); + /** @var RequestEvent|ObjectProphecy $requestEvent */ + $requestEvent = $this->prophesize(RequestEvent::class); + $requestEvent->isMasterRequest() + ->willReturn(true) + ->shouldBeCalled() + ; + $requestEvent->getRequest() + ->willReturn($request->reveal()) + ->shouldBeCalled() + ; + + $request->getContent() + ->willReturn($requestContent) + ->shouldBeCalled() + ; + $request->getRequestUri() + ->willReturn($this->uri) + ->shouldBeCalled() + ; + $request->getMethod() + ->willReturn(Request::METHOD_POST) + ->shouldBeCalled() + ; + + $this->sdkEndpoint->index($requestContent) + ->willReturn($expectedResponseContent) + ->shouldBeCalled(); + + $requestEvent->setResponse(Argument::type(Response::class)) + ->will(function ($args) use (&$actualResponse) { + $actualResponse = $args[0]; + }) + ->shouldBeCalled() + ; + + $this->endpoint->onKernelRequest($requestEvent->reveal()); + + $this->assertInstanceOf(Response::class, $actualResponse); + $this->assertSame(Response::HTTP_OK, $actualResponse->getStatusCode()); + $this->assertSame($expectedResponseContent, $actualResponse->getContent()); + $this->assertSame('application/json', $actualResponse->headers->get('Content-Type')); + } + + public function testHttOptionsShouldReturnAllowedMethodsAndContentType() + { + $expectedAllowedMethodList = implode(', ', [Request::METHOD_POST, Request::METHOD_OPTIONS]); + /** @var null|Request $actualResponse */ + $actualResponse = null; + + /** @var Request|ObjectProphecy $request */ + $request = $this->prophesize(Request::class); + /** @var RequestEvent|ObjectProphecy $requestEvent */ + $requestEvent = $this->prophesize(RequestEvent::class); + $requestEvent->isMasterRequest() + ->willReturn(true) + ->shouldBeCalled() + ; + $requestEvent->getRequest() + ->willReturn($request->reveal()) + ->shouldBeCalled() + ; + $request->getRequestUri() + ->willReturn($this->uri) + ->shouldBeCalled() + ; + $request->getMethod() + ->willReturn(Request::METHOD_OPTIONS) + ->shouldBeCalled() + ; + + $requestEvent->setResponse(Argument::type(Response::class)) + ->will(function ($args) use (&$actualResponse) { + $actualResponse = $args[0]; + }) + ->shouldBeCalled() + ; + + $this->endpoint->onKernelRequest($requestEvent->reveal()); + + $this->assertInstanceOf(Response::class, $actualResponse); + $this->assertSame(Response::HTTP_OK, $actualResponse->getStatusCode()); + $this->assertSame('application/json', $actualResponse->headers->get('Content-Type')); + // Check allowed methods + $this->assertSame($expectedAllowedMethodList, $actualResponse->headers->get('Allow', null)); + $this->assertSame( + $expectedAllowedMethodList, + $actualResponse->headers->get('Access-Control-Request-Method', null) + ); + // Check allowed content types + $this->assertSame('application/json', $actualResponse->headers->get('Accept')); + $this->assertSame('Content-Type', $actualResponse->headers->get('Access-Control-Allow-Headers')); + } + + public function testDoNohingIfNotTheMasterRequest() + { + /** @var RequestEvent|ObjectProphecy $requestEvent */ + $requestEvent = $this->prophesize(RequestEvent::class); + $requestEvent->isMasterRequest() + ->willReturn(false) + ->shouldBeCalled() + ; + + $this->endpoint->onKernelRequest($requestEvent->reveal()); + } + + public function testDoNohingIfNotTheRightUri() + { + /** @var Request|ObjectProphecy $request */ + $request = $this->prophesize(Request::class); + /** @var RequestEvent|ObjectProphecy $requestEvent */ + $requestEvent = $this->prophesize(RequestEvent::class); + $requestEvent->isMasterRequest() + ->willReturn(true) + ->shouldBeCalled() + ; + $requestEvent->getRequest() + ->willReturn($request->reveal()) + ->shouldBeCalled() + ; + $request->getRequestUri() + ->willReturn('/another-uri') + ->shouldBeCalled() + ; + + $this->endpoint->onKernelRequest($requestEvent->reveal()); + } + + + + public function testDoNohingIfHttpMethodNotManaged() + { + /** @var Request|ObjectProphecy $request */ + $request = $this->prophesize(Request::class); + /** @var RequestEvent|ObjectProphecy $requestEvent */ + $requestEvent = $this->prophesize(RequestEvent::class); + $requestEvent->isMasterRequest() + ->willReturn(true) + ->shouldBeCalled() + ; + $requestEvent->getRequest() + ->willReturn($request->reveal()) + ->shouldBeCalled() + ; + $request->getRequestUri() + ->willReturn($this->uri) + ->shouldBeCalled() + ; + + $request->getMethod() + ->willReturn(Request::METHOD_GET) + ->shouldBeCalled() + ; + + $this->endpoint->onKernelRequest($requestEvent->reveal()); + } +}