From 172e9e38cc95a7aa1fabd4a2b7122a7b4b5d6764 Mon Sep 17 00:00:00 2001 From: Alex Pott <alex.a.pott@googlemail.com> Date: Fri, 10 Feb 2017 10:45:01 +0000 Subject: [PATCH] Issue #2815845 by dawehner, Wim Leers, alexpott, tedbow, Berdir, swentel, webflo: Importing (deploying) REST resource config entities should automatically do the necessary route rebuilding --- .../CacheRouterRebuildSubscriber.php | 4 +- .../FinishResponseSubscriber.php | 18 +++++++ core/modules/block/src/Tests/BlockTest.php | 2 + .../src/Tests/Views/DisplayBlockTest.php | 10 ++-- .../menu_ui/src/Tests/MenuCacheTagsTest.php | 3 +- .../node/src/Tests/Views/FrontPageTest.php | 5 +- .../Tests/PageCacheTagsIntegrationTest.php | 2 + .../page_cache/src/Tests/PageCacheTest.php | 2 + core/modules/rest/rest.services.yml | 7 +++ .../rest/src/Entity/RestResourceConfig.php | 19 +++++++ .../EventSubscriber/RestConfigSubscriber.php | 53 +++++++++++++++++++ core/modules/rest/src/Tests/RESTTestBase.php | 3 +- .../Block/BlockResourceTestBase.php | 4 +- .../EntityResource/EntityResourceTestBase.php | 39 ++++++++++---- .../tests/src/Functional/ResourceTestBase.php | 22 +++++++- .../src/Tests/SearchPageCacheTagsTest.php | 9 ++-- .../AssertPageCacheContextsAndTagsTrait.php | 8 ++- .../Tests/Entity/EntityCacheTagsTestBase.php | 4 +- .../system/src/Tests/Routing/RouterTest.php | 10 ++-- .../tour/src/Tests/TourCacheTagsTest.php | 2 + core/modules/views/src/Tests/GlossaryTest.php | 1 + .../FunctionalTests/BrowserTestBaseTest.php | 2 +- 22 files changed, 188 insertions(+), 41 deletions(-) create mode 100644 core/modules/rest/src/EventSubscriber/RestConfigSubscriber.php diff --git a/core/lib/Drupal/Core/EventSubscriber/CacheRouterRebuildSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/CacheRouterRebuildSubscriber.php index 38007f56003d..c1f532799efc 100644 --- a/core/lib/Drupal/Core/EventSubscriber/CacheRouterRebuildSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/CacheRouterRebuildSubscriber.php @@ -16,8 +16,8 @@ class CacheRouterRebuildSubscriber implements EventSubscriberInterface { */ public function onRouterFinished() { // Requested URLs that formerly gave a 403/404 may now be valid. - // Also invalidate all cached routing. - Cache::invalidateTags(['4xx-response', 'route_match']); + // Also invalidate all cached routing as well as every HTTP response. + Cache::invalidateTags(['4xx-response', 'route_match', 'http_response']); } /** diff --git a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php index 3db181adc81c..5898d17ae6a4 100644 --- a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php @@ -88,6 +88,21 @@ public function __construct(LanguageManagerInterface $language_manager, ConfigFa $this->debugCacheabilityHeaders = $http_response_debug_cacheability_headers; } + /** + * Sets extra headers on any responses, also subrequest ones. + * + * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event + * The event to process. + */ + public function onAllResponds(FilterResponseEvent $event) { + $response = $event->getResponse(); + // Always add the 'http_response' cache tag to be able to invalidate every + // response, for example after rebuilding routes. + if ($response instanceof CacheableResponseInterface) { + $response->getCacheableMetadata()->addCacheTags(['http_response']); + } + } + /** * Sets extra headers on successful responses. * @@ -284,6 +299,9 @@ protected function setExpiresNoCache(Response $response) { */ public static function getSubscribedEvents() { $events[KernelEvents::RESPONSE][] = array('onRespond'); + // There is no specific reason for choosing 16 beside it should be executed + // before ::onRespond(). + $events[KernelEvents::RESPONSE][] = array('onAllResponds', 16); return $events; } diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php index 9fd869a92c59..57f46476445e 100644 --- a/core/modules/block/src/Tests/BlockTest.php +++ b/core/modules/block/src/Tests/BlockTest.php @@ -385,6 +385,7 @@ public function testBlockCacheTags() { 'block_view', 'config:block.block.powered', 'config:user.role.anonymous', + 'http_response', 'rendered', ); sort($expected_cache_tags); @@ -426,6 +427,7 @@ public function testBlockCacheTags() { 'config:block.block.powered', 'config:block.block.powered-2', 'config:user.role.anonymous', + 'http_response', 'rendered', ); sort($expected_cache_tags); diff --git a/core/modules/block/src/Tests/Views/DisplayBlockTest.php b/core/modules/block/src/Tests/Views/DisplayBlockTest.php index 368af68ad9f1..d87817526eb7 100644 --- a/core/modules/block/src/Tests/Views/DisplayBlockTest.php +++ b/core/modules/block/src/Tests/Views/DisplayBlockTest.php @@ -262,7 +262,7 @@ public function testBlockRendering() { $result = $this->xpath('//div[contains(@class, "region-sidebar-first")]/div[contains(@class, "block-views")]/h2'); $this->assertTrue(empty($result), 'The title is not visible.'); - $this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:system.site', 'config:views.view.test_view_block' , 'rendered'])); + $this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:system.site', 'config:views.view.test_view_block' , 'http_response', 'rendered'])); } /** @@ -288,7 +288,7 @@ public function testBlockEmptyRendering() { $this->assertEqual(0, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]'))); // Ensure that the view cachability metadata is propagated even, for an // empty block. - $this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:views.view.test_view_block' , 'rendered'])); + $this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:views.view.test_view_block' , 'http_response', 'rendered'])); $this->assertCacheContexts(['url.query_args:_wrapper_format']); // Add a header displayed on empty result. @@ -306,7 +306,7 @@ public function testBlockEmptyRendering() { $this->drupalGet($url); $this->assertEqual(1, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]'))); - $this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:views.view.test_view_block' , 'rendered'])); + $this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:views.view.test_view_block' , 'http_response', 'rendered'])); $this->assertCacheContexts(['url.query_args:_wrapper_format']); // Hide the header on empty results. @@ -324,7 +324,7 @@ public function testBlockEmptyRendering() { $this->drupalGet($url); $this->assertEqual(0, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]'))); - $this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:views.view.test_view_block' , 'rendered'])); + $this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:views.view.test_view_block', 'http_response', 'rendered'])); $this->assertCacheContexts(['url.query_args:_wrapper_format']); // Add an empty text. @@ -341,7 +341,7 @@ public function testBlockEmptyRendering() { $this->drupalGet($url); $this->assertEqual(1, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]'))); - $this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:views.view.test_view_block' , 'rendered'])); + $this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:views.view.test_view_block', 'http_response', 'rendered'])); $this->assertCacheContexts(['url.query_args:_wrapper_format']); } diff --git a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php index 3ad4ad1d40db..bba47d5cc76c 100644 --- a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php +++ b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php @@ -46,6 +46,7 @@ public function testMenuBlock() { // Verify a cache hit, but also the presence of the correct cache tags. $expected_tags = array( + 'http_response', 'rendered', 'block_view', 'config:block_list', @@ -107,7 +108,7 @@ public function testMenuBlock() { $this->verifyPageCache($url, 'MISS'); // Verify a cache hit. - $this->verifyPageCache($url, 'HIT', ['config:block_list', 'config:user.role.anonymous', 'rendered']); + $this->verifyPageCache($url, 'HIT', ['config:block_list', 'config:user.role.anonymous', 'http_response', 'rendered']); } } diff --git a/core/modules/node/src/Tests/Views/FrontPageTest.php b/core/modules/node/src/Tests/Views/FrontPageTest.php index b27c416cb655..19cb659da24a 100644 --- a/core/modules/node/src/Tests/Views/FrontPageTest.php +++ b/core/modules/node/src/Tests/Views/FrontPageTest.php @@ -265,7 +265,7 @@ protected function doTestFrontPageViewCacheTags($do_assert_views_caches) { $render_cache_tags ); $expected_tags = Cache::mergeTags($empty_node_listing_cache_tags, $cache_context_tags); - $expected_tags = Cache::mergeTags($expected_tags, ['rendered', 'config:user.role.anonymous', 'config:system.site']); + $expected_tags = Cache::mergeTags($expected_tags, ['http_response', 'rendered', 'config:user.role.anonymous', 'config:system.site']); $this->assertPageCacheContextsAndTags( Url::fromRoute('view.frontpage.page_1'), $cache_contexts, @@ -331,7 +331,7 @@ protected function doTestFrontPageViewCacheTags($do_assert_views_caches) { $this->assertPageCacheContextsAndTags( Url::fromRoute('view.frontpage.page_1'), $cache_contexts, - Cache::mergeTags($first_page_output_cache_tags, ['rendered', 'config:user.role.anonymous']) + Cache::mergeTags($first_page_output_cache_tags, ['http_response', 'rendered', 'config:user.role.anonymous']) ); // Second page. @@ -350,6 +350,7 @@ protected function doTestFrontPageViewCacheTags($do_assert_views_caches) { 'node_view', 'user_view', 'user:0', + 'http_response', 'rendered', // FinishResponseSubscriber adds this cache tag to responses that have the // 'user.permissions' cache context for anonymous users. diff --git a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php index f9fd3de57192..2c945fdb3d7a 100644 --- a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php +++ b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php @@ -80,6 +80,7 @@ function testPageCacheTags() { // Full node page 1. $this->assertPageCacheContextsAndTags($node_1->urlInfo(), $cache_contexts, array( + 'http_response', 'rendered', 'block_view', 'config:block_list', @@ -120,6 +121,7 @@ function testPageCacheTags() { // Full node page 2. $this->assertPageCacheContextsAndTags($node_2->urlInfo(), $cache_contexts, array( + 'http_response', 'rendered', 'block_view', 'config:block_list', diff --git a/core/modules/page_cache/src/Tests/PageCacheTest.php b/core/modules/page_cache/src/Tests/PageCacheTest.php index 3a7cd17cd4ba..d4fae54a5fca 100644 --- a/core/modules/page_cache/src/Tests/PageCacheTest.php +++ b/core/modules/page_cache/src/Tests/PageCacheTest.php @@ -63,6 +63,7 @@ function testPageCacheTags() { sort($cache_entry->tags); $expected_tags = array( 'config:user.role.anonymous', + 'http_response', 'pre_render', 'rendered', 'system_test_cache_tags_page', @@ -94,6 +95,7 @@ function testPageCacheTagsIndependentFromCacheabilityHeaders() { sort($cache_entry->tags); $expected_tags = array( 'config:user.role.anonymous', + 'http_response', 'pre_render', 'rendered', 'system_test_cache_tags_page', diff --git a/core/modules/rest/rest.services.yml b/core/modules/rest/rest.services.yml index 2def91e992d1..7742e1dbc7a7 100644 --- a/core/modules/rest/rest.services.yml +++ b/core/modules/rest/rest.services.yml @@ -35,8 +35,15 @@ services: logger.channel.rest: parent: logger.channel_base arguments: ['rest'] + + # Event subscribers. rest.resource_response.subscriber: class: Drupal\rest\EventSubscriber\ResourceResponseSubscriber tags: - { name: event_subscriber } arguments: ['@serializer', '@renderer', '@current_route_match'] + rest.config_subscriber: + class: Drupal\rest\EventSubscriber\RestConfigSubscriber + arguments: ['@router.builder'] + tags: + - { name: event_subscriber } diff --git a/core/modules/rest/src/Entity/RestResourceConfig.php b/core/modules/rest/src/Entity/RestResourceConfig.php index bb835841610e..12bd846ea6a6 100644 --- a/core/modules/rest/src/Entity/RestResourceConfig.php +++ b/core/modules/rest/src/Entity/RestResourceConfig.php @@ -3,6 +3,7 @@ namespace Drupal\rest\Entity; use Drupal\Core\Config\Entity\ConfigEntityBase; +use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection; use Drupal\rest\RestResourceConfigInterface; @@ -255,4 +256,22 @@ protected function normalizeRestMethod($method) { return strtoupper($method); } + /** + * {@inheritdoc} + */ + public function postSave(EntityStorageInterface $storage, $update = TRUE) { + parent::postSave($storage, $update); + + \Drupal::service('router.builder')->setRebuildNeeded(); + } + + /** + * {@inheritdoc} + */ + public static function postDelete(EntityStorageInterface $storage, array $entities) { + parent::postDelete($storage, $entities); + + \Drupal::service('router.builder')->setRebuildNeeded(); + } + } diff --git a/core/modules/rest/src/EventSubscriber/RestConfigSubscriber.php b/core/modules/rest/src/EventSubscriber/RestConfigSubscriber.php new file mode 100644 index 000000000000..abab34a86eda --- /dev/null +++ b/core/modules/rest/src/EventSubscriber/RestConfigSubscriber.php @@ -0,0 +1,53 @@ +<?php + +namespace Drupal\rest\EventSubscriber; + +use Drupal\Core\Config\ConfigCrudEvent; +use Drupal\Core\Config\ConfigEvents; +use Drupal\Core\Routing\RouteBuilderInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * A subscriber triggering a route rebuild when certain configuration changes. + */ +class RestConfigSubscriber implements EventSubscriberInterface { + + /** + * The router builder. + * + * @var \Drupal\Core\Routing\RouteBuilderInterface + */ + protected $routerBuilder; + + /** + * Constructs the RestConfigSubscriber. + * + * @param \Drupal\Core\Routing\RouteBuilderInterface $route_builder + * The router builder service. + */ + public function __construct(RouteBuilderInterface $router_builder) { + $this->routerBuilder = $router_builder; + } + + /** + * Informs the router builder a rebuild is needed when necessary. + * + * @param \Drupal\Core\Config\ConfigCrudEvent $event + * The Event to process. + */ + public function onSave(ConfigCrudEvent $event) { + $saved_config = $event->getConfig(); + if ($saved_config->getName() === 'rest.settings' && $event->isChanged('bc_entity_resource_permissions')) { + $this->routerBuilder->setRebuildNeeded(); + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events[ConfigEvents::SAVE][] = ['onSave']; + return $events; + } + +} diff --git a/core/modules/rest/src/Tests/RESTTestBase.php b/core/modules/rest/src/Tests/RESTTestBase.php index 271df515f5ca..405493bccb71 100644 --- a/core/modules/rest/src/Tests/RESTTestBase.php +++ b/core/modules/rest/src/Tests/RESTTestBase.php @@ -420,8 +420,7 @@ protected function enableService($resource_type, $method = 'GET', $format = NULL * Rebuilds routing caches. */ protected function rebuildCache() { - // Rebuild routing cache, so that the REST API paths are available. - $this->container->get('router.builder')->rebuild(); + $this->container->get('router.builder')->rebuildIfNeeded(); } /** diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php index d87e42345c69..0be7622068c0 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php @@ -122,9 +122,9 @@ protected function getExpectedCacheContexts() { protected function getExpectedCacheTags() { // Because the 'user.permissions' cache context is missing, the cache tag // for the anonymous user role is never added automatically. - return array_filter(parent::getExpectedCacheTags(), function ($tag) { + return array_values(array_filter(parent::getExpectedCacheTags(), function ($tag) { return $tag !== 'config:user.role.anonymous'; - }); + })); } /** diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php index 2c9561239580..992e3b0bf17a 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php @@ -146,6 +146,15 @@ protected function provisionEntityResource() { $this->provisionResource('entity.' . static::$entityTypeId, [static::$format], $auth); } + /** + * Deprovisions the tested entity resource. + */ + protected function deprovisionEntityResource() { + $this->resourceConfigStorage->load('entity.' . static::$entityTypeId) + ->delete(); + $this->refreshTestStateAfterRestConfigChange(); + } + /** * {@inheritdoc} */ @@ -195,9 +204,6 @@ public function setUp() { } $this->entity->save(); } - - // @todo Remove this in https://www.drupal.org/node/2815845. - drupal_flush_all_caches(); } /** @@ -291,6 +297,7 @@ protected function getExpectedCacheTags() { if (!static::$auth) { $expected_cache_tags[] = 'config:user.role.anonymous'; } + $expected_cache_tags[] = 'http_response'; return Cache::mergeTags($expected_cache_tags, $this->entity->getCacheTags()); } @@ -458,8 +465,7 @@ public function testGet() { $this->config('rest.settings')->set('bc_entity_resource_permissions', TRUE)->save(TRUE); - // @todo Remove this in https://www.drupal.org/node/2815845. - drupal_flush_all_caches(); + $this->refreshTestStateAfterRestConfigChange(); // DX: 403 when unauthorized. @@ -475,6 +481,20 @@ public function testGet() { $this->assertResourceResponse(200, FALSE, $response); + $this->deprovisionEntityResource(); + + + // DX: upon deprovisioning, immediate 404 if no route, 406 otherwise. + $response = $this->request('GET', $url, $request_options); + if (!$has_canonical_url) { + $this->assertSame(404, $response->getStatusCode()); + } + else { + $this->assert406Response($response); + } + + + $this->provisionEntityResource(); $url->setOption('query', ['_format' => 'non_existing_format']); @@ -673,9 +693,8 @@ public function testPost() { $this->config('rest.settings')->set('bc_entity_resource_permissions', TRUE)->save(TRUE); + $this->refreshTestStateAfterRestConfigChange(); $request_options[RequestOptions::BODY] = $parseable_valid_request_body_2; - // @todo Remove this in https://www.drupal.org/node/2815845. - drupal_flush_all_caches(); // DX: 403 when unauthorized. @@ -876,9 +895,8 @@ public function testPatch() { $this->config('rest.settings')->set('bc_entity_resource_permissions', TRUE)->save(TRUE); + $this->refreshTestStateAfterRestConfigChange(); $request_options[RequestOptions::BODY] = $parseable_valid_request_body_2; - // @todo Remove this in https://www.drupal.org/node/2815845. - drupal_flush_all_caches(); // DX: 403 when unauthorized. @@ -975,8 +993,7 @@ public function testDelete() { $this->config('rest.settings')->set('bc_entity_resource_permissions', TRUE)->save(TRUE); - // @todo Remove this in https://www.drupal.org/node/2815845. - drupal_flush_all_caches(); + $this->refreshTestStateAfterRestConfigChange(); $this->entity = $this->createEntity(); $url = $this->getUrl()->setOption('query', $url->getOption('query')); diff --git a/core/modules/rest/tests/src/Functional/ResourceTestBase.php b/core/modules/rest/tests/src/Functional/ResourceTestBase.php index 2674d705ecbc..d0f601b634a4 100644 --- a/core/modules/rest/tests/src/Functional/ResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/ResourceTestBase.php @@ -119,6 +119,7 @@ public function setUp() { // Ensure there's a clean slate: delete all REST resource config entities. $this->resourceConfigStorage->delete($this->resourceConfigStorage->loadMultiple()); + $this->refreshTestStateAfterRestConfigChange(); } /** @@ -141,8 +142,25 @@ protected function provisionResource($resource_type, $formats = [], $authenticat 'authentication' => $authentication, ] ])->save(); - // @todo Remove this in https://www.drupal.org/node/2815845. - drupal_flush_all_caches(); + $this->refreshTestStateAfterRestConfigChange(); + } + + /** + * Refreshes the state of the tester to be in sync with the testee. + * + * Should be called after every change made to: + * - RestResourceConfig entities + * - the 'rest.settings' simple configuration + */ + protected function refreshTestStateAfterRestConfigChange() { + // Ensure that the cache tags invalidator has its internal values reset. + // Otherwise the http_response cache tag invalidation won't work. + $this->refreshVariables(); + + // Tests using this base class may trigger route rebuilds due to changes to + // RestResourceConfig entities or 'rest.settings'. Ensure the test generates + // routes using an up-to-date router. + \Drupal::service('router.builder')->rebuildIfNeeded(); } /** diff --git a/core/modules/search/src/Tests/SearchPageCacheTagsTest.php b/core/modules/search/src/Tests/SearchPageCacheTagsTest.php index 3a7bc67e745e..667b2135f866 100644 --- a/core/modules/search/src/Tests/SearchPageCacheTagsTest.php +++ b/core/modules/search/src/Tests/SearchPageCacheTagsTest.php @@ -70,6 +70,7 @@ function testSearchText() { $this->assertCacheTag('node:1'); $this->assertCacheTag('user:2'); $this->assertCacheTag('rendered'); + $this->assertCacheTag('http_response'); $this->assertCacheTag('node_list'); // Updating a node should invalidate the search plugin's index cache tag. @@ -83,6 +84,7 @@ function testSearchText() { $this->assertCacheTag('node:1'); $this->assertCacheTag('user:2'); $this->assertCacheTag('rendered'); + $this->assertCacheTag('http_response'); $this->assertCacheTag('node_list'); // Deleting a node should invalidate the search plugin's index cache tag. @@ -172,6 +174,7 @@ public function testSearchTagsBubbling() { 'config:search.page.node_search', 'search_index', 'search_index:node_search', + 'http_response', 'rendered', 'node_list', ]; @@ -192,8 +195,7 @@ public function testSearchTagsBubbling() { 'node_view', 'config:filter.format.plain_text', ]); - $cache_tags = $this->drupalGetHeader('X-Drupal-Cache-Tags'); - $this->assertEqual(explode(' ', $cache_tags), $expected_cache_tags); + $this->assertCacheTags($expected_cache_tags); // Only get the new node in the search results, should result in node:1, // node:2 and user:3 as cache tags even though only node:1 is shown. This is @@ -208,8 +210,7 @@ public function testSearchTagsBubbling() { 'user:3', 'node_view', ]); - $cache_tags = $this->drupalGetHeader('X-Drupal-Cache-Tags'); - $this->assertEqual(explode(' ', $cache_tags), $expected_cache_tags); + $this->assertCacheTags($expected_cache_tags); } } diff --git a/core/modules/system/src/Tests/Cache/AssertPageCacheContextsAndTagsTrait.php b/core/modules/system/src/Tests/Cache/AssertPageCacheContextsAndTagsTrait.php index 94ea1ac44c5b..c216e9bcfd53 100644 --- a/core/modules/system/src/Tests/Cache/AssertPageCacheContextsAndTagsTrait.php +++ b/core/modules/system/src/Tests/Cache/AssertPageCacheContextsAndTagsTrait.php @@ -122,10 +122,14 @@ protected function debugCacheTags(array $actual_tags, array $expected_tags) { */ protected function assertCacheTags(array $expected_tags, $include_default_tags = TRUE) { // The anonymous role cache tag is only added if the user is anonymous. - if ($include_default_tags && \Drupal::currentUser()->isAnonymous()) { - $expected_tags = Cache::mergeTags($expected_tags, ['config:user.role.anonymous']); + if ($include_default_tags) { + if (\Drupal::currentUser()->isAnonymous()) { + $expected_tags = Cache::mergeTags($expected_tags, ['config:user.role.anonymous']); + } + $expected_tags[] = 'http_response'; } $actual_tags = $this->getCacheHeaderValues('X-Drupal-Cache-Tags'); + $expected_tags = array_unique($expected_tags); sort($expected_tags); sort($actual_tags); $this->assertIdentical($actual_tags, $expected_tags); diff --git a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php index 9ecefad4fdcd..c5e9a95e620c 100644 --- a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php +++ b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php @@ -343,7 +343,7 @@ public function testReferencedEntity() { // 'user.permissions' is a required cache context, and responses that vary // by this cache context when requested by anonymous users automatically // also get this cache tag, to ensure correct invalidation. - $page_cache_tags = Cache::mergeTags(['rendered'], ['config:user.role.anonymous']); + $page_cache_tags = Cache::mergeTags(['http_response', 'rendered'], ['config:user.role.anonymous']); // If the block module is used, the Block page display variant is used, // which adds the block config entity type's list cache tags. $page_cache_tags = Cache::mergeTags($page_cache_tags, \Drupal::moduleHandler()->moduleExists('block') ? ['config:block_list'] : []); @@ -641,7 +641,7 @@ public function testReferencedEntity() { // Verify cache hits. $referencing_entity_cache_tags = Cache::mergeTags($this->referencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags()); - $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['rendered']); + $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['http_response', 'rendered']); $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->getAdditionalCacheTagsForEntityListing()); $nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags); diff --git a/core/modules/system/src/Tests/Routing/RouterTest.php b/core/modules/system/src/Tests/Routing/RouterTest.php index 1b33bafdc747..c6aa2b8bc9b5 100644 --- a/core/modules/system/src/Tests/Routing/RouterTest.php +++ b/core/modules/system/src/Tests/Routing/RouterTest.php @@ -45,7 +45,7 @@ public function testFinishResponseSubscriber() { // Check expected headers from FinishResponseSubscriber. $headers = $this->drupalGetHeaders(); $this->assertEqual($headers['x-drupal-cache-contexts'], implode(' ', $expected_cache_contexts)); - $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous rendered'); + $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous http_response rendered'); // Confirm that the page wrapping is being added, so we're not getting a // raw body returned. $this->assertRaw('</html>', 'Page markup was found.'); @@ -60,12 +60,12 @@ public function testFinishResponseSubscriber() { $this->drupalGet('router_test/test18'); $headers = $this->drupalGetHeaders(); $this->assertEqual($headers['x-drupal-cache-contexts'], implode(' ', Cache::mergeContexts($renderer_required_cache_contexts, ['url']))); - $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous foo rendered'); + $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous foo http_response rendered'); // 2. controller result: render array, per-role cacheable route access. $this->drupalGet('router_test/test19'); $headers = $this->drupalGetHeaders(); $this->assertEqual($headers['x-drupal-cache-contexts'], implode(' ', Cache::mergeContexts($renderer_required_cache_contexts, ['url', 'user.roles']))); - $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous foo rendered'); + $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous foo http_response rendered'); // 3. controller result: Response object, globally cacheable route access. $this->drupalGet('router_test/test1'); $headers = $this->drupalGetHeaders(); @@ -80,12 +80,12 @@ public function testFinishResponseSubscriber() { $this->drupalGet('router_test/test21'); $headers = $this->drupalGetHeaders(); $this->assertEqual($headers['x-drupal-cache-contexts'], ''); - $this->assertEqual($headers['x-drupal-cache-tags'], ''); + $this->assertEqual($headers['x-drupal-cache-tags'], 'http_response'); // 6. controller result: CacheableResponse object, per-role cacheable route access. $this->drupalGet('router_test/test22'); $headers = $this->drupalGetHeaders(); $this->assertEqual($headers['x-drupal-cache-contexts'], 'user.roles'); - $this->assertEqual($headers['x-drupal-cache-tags'], ''); + $this->assertEqual($headers['x-drupal-cache-tags'], 'http_response'); // Finally, verify that the X-Drupal-Cache-Contexts and X-Drupal-Cache-Tags // headers are not sent when their container parameter is set to FALSE. diff --git a/core/modules/tour/src/Tests/TourCacheTagsTest.php b/core/modules/tour/src/Tests/TourCacheTagsTest.php index 4f2164e1884c..7a2e87d1c155 100644 --- a/core/modules/tour/src/Tests/TourCacheTagsTest.php +++ b/core/modules/tour/src/Tests/TourCacheTagsTest.php @@ -48,6 +48,7 @@ public function testRenderedTour() { $expected_tags = [ 'config:tour.tour.tour-test', 'config:user.role.anonymous', + 'http_response', 'rendered', ]; $this->verifyPageCache($url, 'HIT', $expected_tags); @@ -68,6 +69,7 @@ public function testRenderedTour() { // Verify a cache hit. $expected_tags = [ 'config:user.role.anonymous', + 'http_response', 'rendered', ]; $this->verifyPageCache($url, 'HIT', $expected_tags); diff --git a/core/modules/views/src/Tests/GlossaryTest.php b/core/modules/views/src/Tests/GlossaryTest.php index 508a4ebc023e..2a5bc907c6ec 100644 --- a/core/modules/views/src/Tests/GlossaryTest.php +++ b/core/modules/views/src/Tests/GlossaryTest.php @@ -95,6 +95,7 @@ public function testGlossaryView() { 'node_list', 'user:0', 'user_list', + 'http_response', 'rendered', // FinishResponseSubscriber adds this cache tag to responses that have the // 'user.permissions' cache context for anonymous users. diff --git a/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php b/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php index 199d25774d1b..25d1c702eb43 100644 --- a/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php +++ b/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php @@ -43,7 +43,7 @@ public function testGoTo() { $this->assertNotContains('</html>', $text); // Response includes cache tags that we can assert. - $this->assertSession()->responseHeaderEquals('X-Drupal-Cache-Tags', 'rendered'); + $this->assertSession()->responseHeaderEquals('X-Drupal-Cache-Tags', 'http_response rendered'); // Test that we can read the JS settings. $js_settings = $this->getDrupalSettings(); -- GitLab