diff --git a/core/modules/jsonapi/src/CacheableResourceResponse.php b/core/modules/jsonapi/src/CacheableResourceResponse.php new file mode 100644 index 0000000000000000000000000000000000000000..162350e60316e6b50e4635008299cdc9c0776664 --- /dev/null +++ b/core/modules/jsonapi/src/CacheableResourceResponse.php @@ -0,0 +1,25 @@ +<?php + +namespace Drupal\jsonapi; + +use Drupal\Core\Cache\CacheableResponseInterface; +use Drupal\Core\Cache\CacheableResponseTrait; + +/** + * Extends ResourceResponse with cacheability. + * + * We want to have the same functionality for both responses that are cacheable + * and those that are not. This response class should be used in all instances + * where the response is expected to be cacheable. + * + * @internal JSON:API maintains no PHP API since its API is the HTTP API. This + * class may change at any time and this will break any dependencies on it. + * + * @see https://www.drupal.org/project/drupal/issues/3032787 + * @see jsonapi.api.php + */ +class CacheableResourceResponse extends ResourceResponse implements CacheableResponseInterface { + + use CacheableResponseTrait; + +} diff --git a/core/modules/jsonapi/src/Controller/EntityResource.php b/core/modules/jsonapi/src/Controller/EntityResource.php index 30d322774ee7129c27a7e4f48c5dca4fefcf77db..c7438feb9c72d3dc7d70b8d3c9dad212208d5687 100644 --- a/core/modules/jsonapi/src/Controller/EntityResource.php +++ b/core/modules/jsonapi/src/Controller/EntityResource.php @@ -6,6 +6,7 @@ use Drupal\Component\Datetime\TimeInterface; use Drupal\Component\Serialization\Json; use Drupal\Core\Cache\CacheableMetadata; +use Drupal\Core\Cache\CacheableResponseInterface; use Drupal\Core\Config\Entity\ConfigEntityInterface; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityFieldManagerInterface; @@ -26,6 +27,7 @@ use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; use Drupal\jsonapi\Access\EntityAccessChecker; +use Drupal\jsonapi\CacheableResourceResponse; use Drupal\jsonapi\Context\FieldResolver; use Drupal\jsonapi\Entity\EntityValidationTrait; use Drupal\jsonapi\Access\TemporaryQueryGuard; @@ -279,7 +281,6 @@ public function createIndividual(ResourceType $resource_type, Request $request) // we should send "Location" header to the frontend. if ($resource_type->isLocatable()) { $url = $resource_object->toUrl()->setAbsolute()->toString(TRUE); - $response->addCacheableDependency($url); $response->headers->set('Location', $url->getGeneratedUrl()); } @@ -536,7 +537,9 @@ function (EntityInterface $entity) { // $response does not contain the entity list cache tag. We add the // cacheable metadata for the finite list of entities in the relationship. - $response->addCacheableDependency($entity); + if ($response instanceof CacheableResponseInterface) { + $response->addCacheableDependency($entity); + } return $response; } @@ -567,7 +570,9 @@ public function getRelationship(ResourceType $resource_type, FieldableEntityInte $relationship = Relationship::createFromEntityReferenceField($resource_object, $field_list); $response = $this->buildWrappedResponse($relationship, $request, $this->getIncludes($request, $resource_object), $response_code); // Add the host entity as a cacheable dependency. - $response->addCacheableDependency($entity); + if ($response instanceof CacheableResponseInterface) { + $response->addCacheableDependency($entity); + } return $response; } @@ -994,7 +999,11 @@ protected function buildWrappedResponse(TopLevelDataInterface $data, Request $re $self_link = new Link(new CacheableMetadata(), self::getRequestLink($request), 'self'); $links = $links->withLink('self', $self_link); } - $response = new ResourceResponse(new JsonApiDocumentTopLevel($data, $includes, $links, $meta), $response_code, $headers); + $document = new JsonApiDocumentTopLevel($data, $includes, $links, $meta); + if (!$request->isMethodCacheable()) { + return new ResourceResponse($document, $response_code, $headers); + } + $response = new CacheableResourceResponse($document, $response_code, $headers); $cacheability = (new CacheableMetadata())->addCacheContexts([ // Make sure that different sparse fieldsets are cached differently. 'url.query_args:fields', diff --git a/core/modules/jsonapi/src/Controller/EntryPoint.php b/core/modules/jsonapi/src/Controller/EntryPoint.php index 29a4276572b12a99abe408bd8c8cd0231a61118b..51444115b22fbcd5ed9fd59114ae58c16bc68077 100644 --- a/core/modules/jsonapi/src/Controller/EntryPoint.php +++ b/core/modules/jsonapi/src/Controller/EntryPoint.php @@ -6,12 +6,12 @@ use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; +use Drupal\jsonapi\CacheableResourceResponse; use Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel; use Drupal\jsonapi\JsonApiResource\LinkCollection; use Drupal\jsonapi\JsonApiResource\NullIncludedData; use Drupal\jsonapi\JsonApiResource\Link; use Drupal\jsonapi\JsonApiResource\ResourceObjectData; -use Drupal\jsonapi\ResourceResponse; use Drupal\jsonapi\ResourceType\ResourceType; use Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -123,7 +123,7 @@ public function index() { } } - $response = new ResourceResponse(new JsonApiDocumentTopLevel(new ResourceObjectData([]), new NullIncludedData(), $urls, $meta)); + $response = new CacheableResourceResponse(new JsonApiDocumentTopLevel(new ResourceObjectData([]), new NullIncludedData(), $urls, $meta)); return $response->addCacheableDependency($cacheability); } diff --git a/core/modules/jsonapi/src/EventSubscriber/DefaultExceptionSubscriber.php b/core/modules/jsonapi/src/EventSubscriber/DefaultExceptionSubscriber.php index e5d3d56ead6fcbc69c5ee32abb313fe6bdbf4858..2967ddc67bb03d8119c2111c82ff453ff0ada292 100644 --- a/core/modules/jsonapi/src/EventSubscriber/DefaultExceptionSubscriber.php +++ b/core/modules/jsonapi/src/EventSubscriber/DefaultExceptionSubscriber.php @@ -2,6 +2,7 @@ namespace Drupal\jsonapi\EventSubscriber; +use Drupal\jsonapi\CacheableResourceResponse; use Drupal\jsonapi\JsonApiResource\ErrorCollection; use Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel; use Drupal\jsonapi\JsonApiResource\LinkCollection; @@ -58,8 +59,14 @@ public function onException(ExceptionEvent $event) { protected function setEventResponse(ExceptionEvent $event, $status) { /* @var \Symfony\Component\HttpKernel\Exception\HttpException $exception */ $exception = $event->getThrowable(); - $response = new ResourceResponse(new JsonApiDocumentTopLevel(new ErrorCollection([$exception]), new NullIncludedData(), new LinkCollection([])), $exception->getStatusCode(), $exception->getHeaders()); - $response->addCacheableDependency($exception); + $document = new JsonApiDocumentTopLevel(new ErrorCollection([$exception]), new NullIncludedData(), new LinkCollection([])); + if ($event->getRequest()->isMethodCacheable()) { + $response = new CacheableResourceResponse($document, $exception->getStatusCode(), $exception->getHeaders()); + $response->addCacheableDependency($exception); + } + else { + $response = new ResourceResponse($document, $exception->getStatusCode(), $exception->getHeaders()); + } $event->setResponse($response); } diff --git a/core/modules/jsonapi/src/EventSubscriber/ResourceResponseSubscriber.php b/core/modules/jsonapi/src/EventSubscriber/ResourceResponseSubscriber.php index 508253fccc1708f53b4ff0c7a9edf87e5f1be1f6..edf81cb677eca4b53f992f74a975519436473f49 100644 --- a/core/modules/jsonapi/src/EventSubscriber/ResourceResponseSubscriber.php +++ b/core/modules/jsonapi/src/EventSubscriber/ResourceResponseSubscriber.php @@ -120,8 +120,10 @@ protected function renderResponseBody(Request $request, ResourceResponse $respon $jsonapi_doc_object = $serializer->normalize($data, $format, $context); // Having just normalized the data, we can associate its cacheability with // the response object. - assert($jsonapi_doc_object instanceof CacheableNormalization); - $response->addCacheableDependency($jsonapi_doc_object); + if ($response instanceof CacheableResponseInterface) { + assert($jsonapi_doc_object instanceof CacheableNormalization); + $response->addCacheableDependency($jsonapi_doc_object); + } // Finally, encode the normalized data (JSON:API's encoder rasterizes it // automatically). $response->setContent($serializer->encode($jsonapi_doc_object->getNormalization(), $format)); diff --git a/core/modules/jsonapi/src/ResourceResponse.php b/core/modules/jsonapi/src/ResourceResponse.php index cf6ddac7a992832d47274bb8cf3539a740c98d34..767e7e71234f5d00e206af0c6dde82f6ce51cdef 100644 --- a/core/modules/jsonapi/src/ResourceResponse.php +++ b/core/modules/jsonapi/src/ResourceResponse.php @@ -2,8 +2,6 @@ namespace Drupal\jsonapi; -use Drupal\Core\Cache\CacheableResponseInterface; -use Drupal\Core\Cache\CacheableResponseTrait; use Symfony\Component\HttpFoundation\Response; /** @@ -22,9 +20,7 @@ * * @see \Drupal\rest\ModifiedResourceResponse */ -class ResourceResponse extends Response implements CacheableResponseInterface { - - use CacheableResponseTrait; +class ResourceResponse extends Response { /** * Response data that should be serialized. diff --git a/core/modules/jsonapi/tests/modules/jsonapi_test_non_cacheable_methods/jsonapi_test_non_cacheable_methods.info.yml b/core/modules/jsonapi/tests/modules/jsonapi_test_non_cacheable_methods/jsonapi_test_non_cacheable_methods.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..a8919e93cbb9b24ca9f80f1d58d8297103672942 --- /dev/null +++ b/core/modules/jsonapi/tests/modules/jsonapi_test_non_cacheable_methods/jsonapi_test_non_cacheable_methods.info.yml @@ -0,0 +1,3 @@ +name: 'JSON API test non-cacheable methods' +type: module +package: Testing diff --git a/core/modules/jsonapi/tests/modules/jsonapi_test_non_cacheable_methods/jsonapi_test_non_cacheable_methods.module b/core/modules/jsonapi/tests/modules/jsonapi_test_non_cacheable_methods/jsonapi_test_non_cacheable_methods.module new file mode 100644 index 0000000000000000000000000000000000000000..cd78294d78a72944460115cfc868b76a769a40dc --- /dev/null +++ b/core/modules/jsonapi/tests/modules/jsonapi_test_non_cacheable_methods/jsonapi_test_non_cacheable_methods.module @@ -0,0 +1,25 @@ +<?php + +/** + * @file + * Contains hook implementations for testing the JSON:API module. + * + * @see: https://www.drupal.org/project/drupal/issues/3072076. + */ + +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Url; + +/** + * Implements hook_entity_presave(). + */ +function jsonapi_test_non_cacheable_methods_entity_presave(EntityInterface $entity) { + Url::fromRoute('<front>')->toString(); +} + +/** + * Implements hook_entity_predelete(). + */ +function jsonapi_test_non_cacheable_methods_entity_predelete(EntityInterface $entity) { + Url::fromRoute('<front>')->toString(); +} diff --git a/core/modules/jsonapi/tests/src/Functional/JsonApiRegressionTest.php b/core/modules/jsonapi/tests/src/Functional/JsonApiRegressionTest.php index 949da0a6a747b3002fe192f60eef90c64b123ce8..c6f9c1b3b2a67f8a86b9bc9e126df4d079ef1da7 100644 --- a/core/modules/jsonapi/tests/src/Functional/JsonApiRegressionTest.php +++ b/core/modules/jsonapi/tests/src/Functional/JsonApiRegressionTest.php @@ -1299,4 +1299,67 @@ public function testAliasedFieldsWithVirtualRelationships() { $this->assertSame(200, $response->getStatusCode()); } + /** + * Tests that caching isn't happening for non-cacheable methods. + * + * @see https://www.drupal.org/project/drupal/issues/3072076 + */ + public function testNonCacheableMethods() { + $this->container->get('module_installer')->install([ + 'jsonapi_test_non_cacheable_methods', + ], TRUE); + $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); + + $node = Node::create([ + 'type' => 'article', + 'title' => 'Llama non-cacheable', + ]); + $node->save(); + + $user = $this->drupalCreateUser([ + 'access content', + 'create article content', + 'edit any article content', + 'delete any article content', + ]); + $base_request_options = [ + RequestOptions::HEADERS => [ + 'Content-Type' => 'application/vnd.api+json', + 'Accept' => 'application/vnd.api+json', + ], + RequestOptions::AUTH => [$user->getAccountName(), $user->pass_raw], + ]; + $methods = [ + 'HEAD', + 'GET', + 'PATCH', + 'DELETE', + ]; + $non_post_request_options = $base_request_options + [ + RequestOptions::JSON => [ + 'data' => [ + 'type' => 'node--article', + 'id' => $node->uuid(), + ], + ], + ]; + foreach ($methods as $method) { + $response = $this->request($method, Url::fromUri('internal:/jsonapi/node/article/' . $node->uuid()), $non_post_request_options); + $this->assertSame($method === 'DELETE' ? 204 : 200, $response->getStatusCode()); + } + + $post_request_options = $base_request_options + [ + RequestOptions::JSON => [ + 'data' => [ + 'type' => 'node--article', + 'attributes' => [ + 'title' => 'Llama non-cacheable', + ], + ], + ], + ]; + $response = $this->request('POST', Url::fromUri('internal:/jsonapi/node/article'), $post_request_options); + $this->assertSame(201, $response->getStatusCode()); + } + } diff --git a/core/modules/jsonapi/tests/src/Functional/ResourceResponseTestTrait.php b/core/modules/jsonapi/tests/src/Functional/ResourceResponseTestTrait.php index 4c10cf80f6c1480851a4361714764a5be7f621f3..3d5665f48d0b190f8efb206cbd20651337552eda 100644 --- a/core/modules/jsonapi/tests/src/Functional/ResourceResponseTestTrait.php +++ b/core/modules/jsonapi/tests/src/Functional/ResourceResponseTestTrait.php @@ -11,8 +11,8 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\RevisionableInterface; use Drupal\Core\Url; +use Drupal\jsonapi\CacheableResourceResponse; use Drupal\jsonapi\Normalizer\HttpExceptionNormalizer; -use Drupal\jsonapi\ResourceResponse; use Psr\Http\Message\ResponseInterface; /** @@ -38,7 +38,7 @@ trait ResourceResponseTestTrait { * be deduced from the number of responses, because a multiple cardinality * field may have only one value. * - * @return \Drupal\jsonapi\ResourceResponse + * @return \Drupal\jsonapi\CacheableResourceResponse * The merged ResourceResponse. */ protected static function toCollectionResourceResponse(array $responses, $self_link, $is_multiple) { @@ -103,7 +103,7 @@ protected static function toCollectionResourceResponse(array $responses, $self_l // individual resources in those collections, which means any '4xx-response' // cache tags on the individual responses should also be omitted. $merged_cacheability->setCacheTags(array_diff($merged_cacheability->getCacheTags(), ['4xx-response'])); - return (new ResourceResponse($merged_document, 200))->addCacheableDependency($merged_cacheability); + return (new CacheableResourceResponse($merged_document, 200))->addCacheableDependency($merged_cacheability); } /** @@ -203,7 +203,7 @@ protected function getExpectedIncludedResourceResponse(array $include_paths, arr $basic_cacheability = (new CacheableMetadata()) ->addCacheTags($this->getExpectedCacheTags()) ->addCacheContexts($this->getExpectedCacheContexts()); - return static::decorateExpectedResponseForIncludedFields(new ResourceResponse($individual_document), $resource_data['responses']) + return static::decorateExpectedResponseForIncludedFields(new CacheableResourceResponse($individual_document), $resource_data['responses']) ->addCacheableDependency($basic_cacheability); } @@ -230,7 +230,7 @@ protected static function toResourceResponses(array $responses) { * @param \Psr\Http\Message\ResponseInterface $response * A PSR response to be mapped. * - * @return \Drupal\jsonapi\ResourceResponse + * @return \Drupal\jsonapi\CacheableResourceResponse * The ResourceResponse. */ protected static function toResourceResponse(ResponseInterface $response) { @@ -245,7 +245,7 @@ protected static function toResourceResponse(ResponseInterface $response) { $cacheability->setCacheMaxAge(($dynamic_cache[0] === 'UNCACHEABLE' && $response->getStatusCode() < 400) ? 0 : Cache::PERMANENT); } $related_document = Json::decode($response->getBody()); - $resource_response = new ResourceResponse($related_document, $response->getStatusCode()); + $resource_response = new CacheableResourceResponse($related_document, $response->getStatusCode()); return $resource_response->addCacheableDependency($cacheability); } @@ -496,7 +496,7 @@ protected function getResponses(array $links, array $request_options) { * (optional) Document pointer for the JSON:API error object. FALSE to omit * the pointer. * - * @return \Drupal\jsonapi\ResourceResponse + * @return \Drupal\jsonapi\CacheableResourceResponse * The forbidden ResourceResponse. */ protected static function getAccessDeniedResponse(EntityInterface $entity, AccessResultInterface $access, Url $via_link, $relationship_field_name = NULL, $detail = NULL, $pointer = NULL) { @@ -519,7 +519,7 @@ protected static function getAccessDeniedResponse(EntityInterface $entity, Acces $error['links']['via']['href'] = $via_link->setAbsolute()->toString(); } - return (new ResourceResponse([ + return (new CacheableResourceResponse([ 'jsonapi' => static::$jsonApiMember, 'errors' => [$error], ], 403)) @@ -536,7 +536,7 @@ protected static function getAccessDeniedResponse(EntityInterface $entity, Acces * @param string $self_link * The self link for collection ResourceResponse. * - * @return \Drupal\jsonapi\ResourceResponse + * @return \Drupal\jsonapi\CacheableResourceResponse * The empty collection ResourceResponse. */ protected function getEmptyCollectionResponse($cardinality, $self_link) { @@ -549,7 +549,7 @@ protected function getEmptyCollectionResponse($cardinality, $self_link) { 'url.site', ], $this->entity->getEntityType()->isRevisionable() ? ['url.query_args:resourceVersion'] : []); $cacheability = (new CacheableMetadata())->addCacheContexts($cache_contexts)->addCacheTags(['http_response']); - return (new ResourceResponse([ + return (new CacheableResourceResponse([ // Empty to-one relationships should be NULL and empty to-many // relationships should be an empty array. 'data' => $cardinality === 1 ? NULL : [], diff --git a/core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php b/core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php index 40349b3e88a50d1fe66c8a81b5265e0497d995ad..94e80f589eb0a3ca36fd180057aee2be9fed185b 100644 --- a/core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php +++ b/core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php @@ -27,6 +27,7 @@ use Drupal\Core\Url; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; +use Drupal\jsonapi\CacheableResourceResponse; use Drupal\jsonapi\JsonApiResource\LinkCollection; use Drupal\jsonapi\JsonApiResource\NullIncludedData; use Drupal\jsonapi\JsonApiResource\Link; @@ -1253,7 +1254,7 @@ protected function getExpectedCollectionResponse(array $collection, $self_link, $cacheability = static::getExpectedCollectionCacheability($this->account, $collection, NULL, $filtered); $cacheability->setCacheMaxAge($merged_response->getCacheableMetadata()->getCacheMaxAge()); - $collection_response = new ResourceResponse($merged_document); + $collection_response = new CacheableResourceResponse($merged_document); $collection_response->addCacheableDependency($cacheability); if (is_null($included_paths)) { @@ -1668,7 +1669,7 @@ protected function doTestRelationshipMutation(array $request_options) { * @param \Drupal\Core\Entity\EntityInterface|null $entity * (optional) The entity for which to get expected relationship response. * - * @return \Drupal\jsonapi\ResourceResponse + * @return \Drupal\jsonapi\CacheableResourceResponse * The expected ResourceResponse. */ protected function getExpectedGetRelationshipResponse($relationship_field_name, EntityInterface $entity = NULL) { @@ -1693,7 +1694,7 @@ protected function getExpectedGetRelationshipResponse($relationship_field_name, ->addCacheableDependency($entity) ->addCacheableDependency($access); $status_code = isset($expected_document['errors'][0]['status']) ? $expected_document['errors'][0]['status'] : 200; - $resource_response = new ResourceResponse($expected_document, $status_code); + $resource_response = new CacheableResourceResponse($expected_document, $status_code); $resource_response->addCacheableDependency($expected_cacheability); return $resource_response; } @@ -1840,7 +1841,7 @@ protected function getExpectedRelatedResponse($relationship_field_name, array $r $cacheability = (new CacheableMetadata())->addCacheContexts($cache_contexts)->addCacheTags(['http_response']); $related_response = isset($relationship_document['errors']) ? $relationship_response - : (new ResourceResponse(static::getEmptyCollectionResponse(!is_null($relationship_document['data']), $self_link)->getResponseData()))->addCacheableDependency($cacheability); + : (new CacheableResourceResponse(static::getEmptyCollectionResponse(!is_null($relationship_document['data']), $self_link)->getResponseData()))->addCacheableDependency($cacheability); } else { $is_to_one_relationship = static::isResourceIdentifier($relationship_document['data']); @@ -3171,15 +3172,15 @@ public function testRevisions() { * the expected cacheability for those includes. It does so based of responses * from the related routes for individual relationships. * - * @param \Drupal\jsonapi\ResourceResponse $expected_response + * @param \Drupal\jsonapi\CacheableResourceResponse $expected_response * The expected ResourceResponse. * @param \Drupal\jsonapi\ResourceResponse[] $related_responses * The related ResourceResponses, keyed by relationship field names. * - * @return \Drupal\jsonapi\ResourceResponse + * @return \Drupal\jsonapi\CacheableResourceResponse * The decorated ResourceResponse. */ - protected static function decorateExpectedResponseForIncludedFields(ResourceResponse $expected_response, array $related_responses) { + protected static function decorateExpectedResponseForIncludedFields(CacheableResourceResponse $expected_response, array $related_responses) { $expected_document = $expected_response->getResponseData(); $expected_cacheability = $expected_response->getCacheableMetadata(); foreach ($related_responses as $related_response) { @@ -3206,17 +3207,17 @@ protected static function decorateExpectedResponseForIncludedFields(ResourceResp } } } - return (new ResourceResponse($expected_document))->addCacheableDependency($expected_cacheability); + return (new CacheableResourceResponse($expected_document))->addCacheableDependency($expected_cacheability); } /** * Gets the expected individual ResourceResponse for GET. * - * @return \Drupal\jsonapi\ResourceResponse + * @return \Drupal\jsonapi\CacheableResourceResponse * The expected individual ResourceResponse. */ protected function getExpectedGetIndividualResourceResponse($status_code = 200) { - $resource_response = new ResourceResponse($this->getExpectedDocument(), $status_code); + $resource_response = new CacheableResourceResponse($this->getExpectedDocument(), $status_code); $cacheability = new CacheableMetadata(); $cacheability->setCacheContexts($this->getExpectedCacheContexts()); $cacheability->setCacheTags($this->getExpectedCacheTags()); diff --git a/core/modules/jsonapi/tests/src/Kernel/Controller/EntityResourceTest.php b/core/modules/jsonapi/tests/src/Kernel/Controller/EntityResourceTest.php index e583518f76239e609a5764c12eccdc4939ece013..5f0246f2cbc0092804fd0914fe9f2c2d5c28c9d8 100644 --- a/core/modules/jsonapi/tests/src/Kernel/Controller/EntityResourceTest.php +++ b/core/modules/jsonapi/tests/src/Kernel/Controller/EntityResourceTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\jsonapi\Kernel\Controller; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\jsonapi\CacheableResourceResponse; use Drupal\jsonapi\ResourceType\ResourceType; use Drupal\jsonapi\JsonApiResource\Data; use Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel; @@ -200,6 +201,7 @@ public function testGetPagedCollection() { $response = $entity_resource->getCollection($resource_type, $request); // Assertions. + $this->assertInstanceOf(CacheableResourceResponse::class, $response); $this->assertInstanceOf(JsonApiDocumentTopLevel::class, $response->getResponseData()); $this->assertInstanceOf(Data::class, $response->getResponseData()->getData()); $data = $response->getResponseData()->getData(); @@ -220,6 +222,7 @@ public function testGetEmptyCollection() { $response = $this->entityResource->getCollection($resource_type, $request); // Assertions. + $this->assertInstanceOf(CacheableResourceResponse::class, $response); $this->assertInstanceOf(JsonApiDocumentTopLevel::class, $response->getResponseData()); $this->assertInstanceOf(Data::class, $response->getResponseData()->getData()); $this->assertEquals(0, $response->getResponseData()->getData()->count());