.../EventSubscriber/ResourceResponseSubscriber.php | 9 +-- .../ResourceResponseSubscriberTest.php | 77 ++++++++++++++++------ 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php b/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php index ce77e01..514f4ee 100644 --- a/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php +++ b/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php @@ -71,7 +71,7 @@ public function onRespond(FilterResponseEvent $event) { $request = $event->getRequest(); $format = $this->getResponseFormat($this->routeMatch, $request); - $response = $this->renderResponseBody($request, $response, $this->serializer, $format); + $this->renderResponseBody($request, $response, $this->serializer, $format); $event->setResponse($this->flattenResponse($response)); } @@ -142,9 +142,6 @@ public function getResponseFormat(RouteMatchInterface $route_match, Request $req * The response format, or NULL in case the response does not need a format, * for example for the response to a DELETE request. * - * @return \Drupal\rest\ResourceResponse - * The altered response. - * * @todo Add test coverage for language negotiation contexts in * https://www.drupal.org/node/2135829. */ @@ -171,8 +168,6 @@ protected function renderResponseBody(Request $request, ResourceResponseInterfac $response->setContent($output); $response->headers->set('Content-Type', $request->getMimeType($format)); } - - return $response; } /** @@ -191,6 +186,8 @@ protected function flattenResponse(ResourceResponseInterface $response) { $final_response = ($response instanceof CacheableResponseInterface) ? new CacheableResponse() : new Response(); $final_response->setContent($response->getContent()); $final_response->setStatusCode($response->getStatusCode()); + $final_response->setProtocolVersion($response->getProtocolVersion()); + $final_response->setCharset($response->getCharset()); $final_response->headers->add($response->headers->all()); if ($final_response instanceof CacheableResponseInterface) { $final_response->addCacheableDependency($response->getCacheableMetadata()); diff --git a/core/modules/rest/tests/src/Unit/EventSubscriber/ResourceResponseSubscriberTest.php b/core/modules/rest/tests/src/Unit/EventSubscriber/ResourceResponseSubscriberTest.php index bb138fe..808f33d 100644 --- a/core/modules/rest/tests/src/Unit/EventSubscriber/ResourceResponseSubscriberTest.php +++ b/core/modules/rest/tests/src/Unit/EventSubscriber/ResourceResponseSubscriberTest.php @@ -9,7 +9,7 @@ use Drupal\Core\Routing\RouteMatch; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\rest\EventSubscriber\ResourceResponseSubscriber; -use Drupal\rest\Plugin\ResourceBase; +use Drupal\rest\ModifiedResourceResponse; use Drupal\rest\ResourceResponse; use Drupal\rest\ResourceResponseInterface; use Drupal\serialization\Encoder\JsonEncoder; @@ -25,7 +25,7 @@ /** * @coversDefaultClass \Drupal\rest\EventSubscriber\ResourceResponseSubscriber - * @group restkak + * @group rest */ class ResourceResponseSubscriberTest extends UnitTestCase { @@ -113,7 +113,7 @@ public function testResponseFormat($methods, array $supported_formats, $request_ * * @dataProvider providerTestResponseFormat */ - public function testOnRespond($methods, array $supported_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) { + public function testOnRespondWithCacheableResponse($methods, array $supported_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) { $rest_config_name = $this->randomMachineName(); $parameters = []; @@ -132,14 +132,13 @@ public function testOnRespond($methods, array $supported_formats, $request_forma $route_requirement_key_format = $request->isMethodSafe() ? '_format' : '_content_type_format'; $route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $rest_config_name], [$route_requirement_key_format => implode('|', $supported_formats)])); - // The RequestHandler must return a ResourceResponseInterface object. $handler_response = new ResourceResponse($method !== 'DELETE' ? ['REST' => 'Drupal'] : NULL); $this->assertInstanceOf(ResourceResponseInterface::class, $handler_response); $this->assertInstanceOf(CacheableResponseInterface::class, $handler_response); // The ResourceResponseSubscriber must then generate a response body and - // transform it to a plain (Cacheable)Response object. + // transform it to a plain CacheableResponse object. $resource_response_subscriber = $this->getFunctioningResourceResponseSubscriber($route_match); $event = new FilterResponseEvent( $this->prophesize(HttpKernelInterface::class)->reveal(), @@ -150,9 +149,59 @@ public function testOnRespond($methods, array $supported_formats, $request_forma $resource_response_subscriber->onRespond($event); $final_response = $event->getResponse(); $this->assertNotInstanceOf(ResourceResponseInterface::class, $final_response); - $this->assertInstanceOf(CacheableResponseInterface::class, $handler_response); - $this->assertSame($expected_response_content_type, $handler_response->headers->get('Content-Type')); - $this->assertEquals($expected_response_content, $handler_response->getContent()); + $this->assertInstanceOf(CacheableResponseInterface::class, $final_response); + $this->assertSame($expected_response_content_type, $final_response->headers->get('Content-Type')); + $this->assertEquals($expected_response_content, $final_response->getContent()); + } + } + + /** + * @covers ::onRespond + * @covers ::getResponseFormat + * @covers ::renderResponseBody + * @covers ::flattenResponse + * + * @dataProvider providerTestResponseFormat + */ + public function testOnRespondWithUncacheableResponse($methods, array $supported_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) { + $rest_config_name = $this->randomMachineName(); + + $parameters = []; + if ($request_format !== FALSE) { + $parameters['_format'] = $request_format; + } + + foreach ($request_headers as $key => $value) { + unset($request_headers[$key]); + $key = strtoupper(str_replace('-', '_', $key)); + $request_headers[$key] = $value; + } + + foreach ($methods as $method) { + $request = Request::create('/rest/test', $method, $parameters, [], [], $request_headers, $request_body); + $route_requirement_key_format = $request->isMethodSafe() ? '_format' : '_content_type_format'; + $route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $rest_config_name], [$route_requirement_key_format => implode('|', $supported_formats)])); + + // The RequestHandler must return a ResourceResponseInterface object. + $handler_response = new ModifiedResourceResponse($method !== 'DELETE' ? ['REST' => 'Drupal'] : NULL); + $this->assertInstanceOf(ResourceResponseInterface::class, $handler_response); + $this->assertNotInstanceOf(CacheableResponseInterface::class, $handler_response); + + // The ResourceResponseSubscriber must then generate a response body and + // transform it to a plain Response object. + $resource_response_subscriber = $this->getFunctioningResourceResponseSubscriber($route_match); + $event = new FilterResponseEvent( + $this->prophesize(HttpKernelInterface::class)->reveal(), + $request, + HttpKernelInterface::MASTER_REQUEST, + $handler_response + ); + $resource_response_subscriber->onRespond($event); + $final_response = $event->getResponse(); + $this->assertNotInstanceOf(ResourceResponseInterface::class, $final_response); + $this->assertNotInstanceOf(CacheableResponseInterface::class, $final_response); + $this->assertSame($expected_response_content_type, $final_response->headers->get('Content-Type')); + $this->assertEquals($expected_response_content, $final_response->getContent()); } } @@ -320,15 +369,3 @@ public function providerTestResponseFormat() { } } - -/** - * Stub class where we can prophesize methods. - */ -class StubRequestHandlerResourcePlugin extends ResourceBase { - - function get() {} - function post() {} - function patch() {} - function delete() {} - -}