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/Endpoint/JsonRpcHttpEndpoint.php b/src/EventListener/RequestListener.php similarity index 56% rename from src/Endpoint/JsonRpcHttpEndpoint.php rename to src/EventListener/RequestListener.php index 3aae8a3..956022e 100644 --- a/src/Endpoint/JsonRpcHttpEndpoint.php +++ b/src/EventListener/RequestListener.php @@ -1,34 +1,48 @@ uri = $uri; $this->sdkEndpoint = $sdkEndpoint; $this->allowedMethodList = [Request::METHOD_POST, Request::METHOD_OPTIONS]; } - /** - * @return Response - */ - public function httpOptions() : Response + 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'); @@ -45,12 +59,7 @@ public function httpOptions() : Response return $response; } - /** - * @param Request $request - * - * @return Response - */ - public function httpPost(Request $request) : Response + protected function httpPost(Request $request) : Response { $response = new Response(); $response->headers->set('Content-Type', 'application/json'); 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.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 468d994..bd8ed0a 100644 --- a/src/Resources/config/services.public.yaml +++ b/src/Resources/config/services.public.yaml @@ -2,11 +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.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 0f982f9..5b54039 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; /** @@ -38,6 +39,7 @@ protected function getContainerExtensions(): array * @dataProvider provideSDKAppServiceIdAndClass * @dataProvider provideSDKInfraServiceIdAndClass * @dataProvider provideBundlePublicServiceIdAndClass + * @dataProvider provideBundlePrivateServiceIdAndClass * * @param string $serviceId * @param string $expectedClassName @@ -123,11 +125,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, @@ -142,15 +139,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 3f76751..0000000 --- a/tests/Functional/Endpoint/JsonRpcHttpEndpointTest.php +++ /dev/null @@ -1,77 +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()); + } +}