diff --git a/src/Plugin/views/query/SearchApiQuery.php b/src/Plugin/views/query/SearchApiQuery.php index 66513f20..ce857a22 100644 --- a/src/Plugin/views/query/SearchApiQuery.php +++ b/src/Plugin/views/query/SearchApiQuery.php @@ -761,9 +761,41 @@ class SearchApiQuery extends QueryPluginBase { * {@inheritdoc} */ public function getCacheTags() { - return $this->getIndex()->getCacheTagsToInvalidate(); + $tags = parent::getCacheTags(); + + if ($this->getIndex()->getOption('index_directly')) { + // @todo If the entities are indexed directly we can add the cache tags + // of the entities themselves. + } + else { + // @todo If we are indexing asynchronously the best we can do is + // invalidate the result when the index changes. + // $this->getIndex()->getEntityType()->getListCacheTags(); + } + + // If the configuration of the search index changes we should invalidate the + // views that show results from this index. + $tags = Cache::mergeTags($this->getIndex()->getCacheTagsToInvalidate(), $tags); + + return $tags; + } + + /** + * {@inheritdoc} + */ + public function getCacheMaxAge() { + $max_age = parent::getCacheMaxAge(); + + // @todo We should get the max ages from the viewed items. + // @ref \Drupal\views\Plugin\views\query\Sql::getCacheMaxAge() + // foreach ($this->getAllEntities() as $entity) { + // $max_age = Cache::mergeMaxAges($max_age, $entity->getCacheMaxAge()); + // } + + return $max_age; } + /** * Retrieves the conditions placed on this query. * diff --git a/tests/search_api_test/search_api_test.module b/tests/search_api_test/search_api_test.module index deeb41a7..eb14868a 100644 --- a/tests/search_api_test/search_api_test.module +++ b/tests/search_api_test/search_api_test.module @@ -14,7 +14,7 @@ use Drupal\node\NodeInterface; function search_api_test_node_grants(AccountInterface $account, $op) { $grants = []; - if (\Drupal::state()->get('search_api_test_add_node_access_grant', TRUE)) { + if (\Drupal::state()->get('search_api_test_add_node_access_grant', FALSE)) { $grants['search_api_test'] = [$account->id()]; } @@ -27,7 +27,7 @@ function search_api_test_node_grants(AccountInterface $account, $op) { function search_api_test_node_access_records(NodeInterface $node) { $grants = []; - if (\Drupal::state()->get('search_api_test_add_node_access_grant', TRUE)) { + if (\Drupal::state()->get('search_api_test_add_node_access_grant', FALSE)) { $grants[] = [ 'realm' => 'search_api_test', 'gid' => $node->getOwnerId(), diff --git a/tests/src/Kernel/Processor/ContentAccessTest.php b/tests/src/Kernel/Processor/ContentAccessTest.php index 0d287112..df59c584 100644 --- a/tests/src/Kernel/Processor/ContentAccessTest.php +++ b/tests/src/Kernel/Processor/ContentAccessTest.php @@ -48,6 +48,9 @@ class ContentAccessTest extends ProcessorTestBase { public function setUp($processor = NULL) { parent::setUp('content_access'); + // Activate our custom grant. + \Drupal::state()->set('search_api_test_add_node_access_grant', TRUE); + // Create a node type for testing. $type = NodeType::create(['type' => 'page', 'name' => 'page']); $type->save(); diff --git a/tests/src/Kernel/Views/ViewsCacheInvalidationTest.php b/tests/src/Kernel/Views/ViewsCacheInvalidationTest.php index 6eb8197b..d0d29965 100644 --- a/tests/src/Kernel/Views/ViewsCacheInvalidationTest.php +++ b/tests/src/Kernel/Views/ViewsCacheInvalidationTest.php @@ -4,7 +4,6 @@ namespace Drupal\Tests\search_api\Kernel\Views; use Drupal\Core\Cache\Cache; use Drupal\Core\DependencyInjection\ContainerBuilder; -use Drupal\Core\Session\AccountInterface; use Drupal\KernelTests\KernelTestBase; use Drupal\node\Entity\Node; use Drupal\node\Entity\NodeType; @@ -67,6 +66,13 @@ class ViewsCacheInvalidationTest extends KernelTestBase { */ protected $cacheTagsInvalidator; + /** + * The current user service. + * + * @var \Drupal\Core\Session\AccountProxyInterface + */ + protected $currentUser; + /** * The search index used for testing. * @@ -114,23 +120,6 @@ class ViewsCacheInvalidationTest extends KernelTestBase { 'views', ]; - /** - * {@inheritdoc} - */ - public function register(ContainerBuilder $container) { - parent::register($container); - - // By default core defines the 'languages:language_interface', 'theme', and - // 'user.permissions' cache contexts as required for every request, to - // ensure that every page is rendered in the right variant according to the - // user's language, theme and role. However this makes it more difficult for - // us to assert whether a cache context has been set by Search API or core. - // By disabling all required contexts we have a clean slate to test with. - $renderer_config = $container->getParameter('renderer.config'); - $renderer_config['required_cache_contexts'] = []; - $container->setParameter('renderer.config', $renderer_config); - } - /** * {@inheritdoc} */ @@ -157,6 +146,7 @@ class ViewsCacheInvalidationTest extends KernelTestBase { $this->renderer = $this->container->get('renderer'); $this->renderCache = $this->container->get('render_cache'); $this->cacheTagsInvalidator = $this->container->get('cache_tags.invalidator'); + $this->currentUser = $this->container->get('current_user'); // Use the test search index from the search_api_test_db module. $this->index = Index::load('test_node_index'); @@ -174,6 +164,11 @@ class ViewsCacheInvalidationTest extends KernelTestBase { } $this->index->indexItems(); + // Create a dummy test user. This user will get UID 1 which is handled as + // the root user and can bypass all access restrictions. This is not used + // in the test. + $this->createUser(); + // Create two test users, one with permission to view unpublished entities, // and one without. $this->users['no-access'] = $this->createUser(['access content']); @@ -299,7 +294,7 @@ class ViewsCacheInvalidationTest extends KernelTestBase { * The key of the user for which to perform the check. */ protected function doAssertCached($assert_method, $user_key) { - $this->setCurrentUser($this->users[$user_key]); + $this->setCurrentUser($user_key); // Retrieve the view to render, and apply the required cache contexts that // are also applied when RendererInterface::renderRoot() is executed. This @@ -312,6 +307,14 @@ class ViewsCacheInvalidationTest extends KernelTestBase { $this->$assert_method($cached_data); } + /** + * Checks that the view for the given user contains the expected results. + * + * @param string $user_key + * The key of the user to check. + * @param array $node_keys + * The keys of the nodes that are expected to be present in the result set. + */ protected function assertViewsResult($user_key, array $node_keys) { // Clear the static caches of the cache tags invalidators. The invalidators // will only invalidate cache tags once per request to improve performance. @@ -321,7 +324,7 @@ class ViewsCacheInvalidationTest extends KernelTestBase { // been requested, and the static caches are empty. $this->cacheTagsInvalidator->resetChecksums(); - $this->setCurrentUser($this->users[$user_key]); + $this->setCurrentUser($user_key); $render_array = $this->getRenderableView(); $html = (string) $this->renderer->renderRoot($render_array); @@ -341,22 +344,28 @@ class ViewsCacheInvalidationTest extends KernelTestBase { } } - protected function setCurrentUser(AccountInterface $user) { - // @todo access on property. - \Drupal::currentUser()->setAccount($user); + /** + * Sets the user with the given key as the currently active user. + * + * @param string $user_key + * The key of the user to set as currently active user. + */ + protected function setCurrentUser($user_key) { + $this->currentUser->setAccount($this->users[$user_key]); } - protected function getView() { + /** + * Returns the test view as a render array. + * + * @return array|null + * The render array, or NULL if the view cannot be rendered. + */ + protected function getRenderableView() { /** @var \Drupal\views\ViewEntityInterface $view */ $view = $this->entityTypeManager->getStorage('view')->load(self::TEST_VIEW_ID); $executable = $this->viewExecutableFactory->get($view); $executable->setDisplay(self::TEST_VIEW_DISPLAY_ID); - //$executable->setExposedInput(['search_api_fulltext' => 'Glaive']); - return $executable; - } - - protected function getRenderableView() { - $render_array = $this->getView()->buildRenderable(); + $render_array = $executable->buildRenderable(); $render_array['#cache']['contexts'] = Cache::mergeContexts($render_array['#cache']['contexts'], $this->container->getParameter('renderer.config')['required_cache_contexts']); return $render_array; diff --git a/tests/src/Kernel/Views/ViewsDisplayCachingTest.php b/tests/src/Kernel/Views/ViewsDisplayCachingTest.php index ce6d8505..5fccebc7 100644 --- a/tests/src/Kernel/Views/ViewsDisplayCachingTest.php +++ b/tests/src/Kernel/Views/ViewsDisplayCachingTest.php @@ -317,9 +317,14 @@ class ViewsDisplayCachingTest extends KernelTestBase { // Views. This is expected to disable caching. [ 'none', - // It is expected that only the configuration of the view itself is - // available as a cache tag. [ + // It is expected that the configuration of the search index is + // available as a cache tag, so that the caches are invalidated if the + // configuration of the search index changes. + 'config:search_api.index.database_search_index', + // It is expected that the configuration of the view itself is + // available as a cache tag, so that the caches are invalidated if the + // view configuration changes. 'config:views.view.search_api_test_cache', ], // No specific cache contexts are expected to be present. @@ -337,6 +342,10 @@ class ViewsDisplayCachingTest extends KernelTestBase { [ 'tag', [ + // It is expected that the configuration of the search index is + // available as a cache tag, so that the caches are invalidated if the + // configuration of the search index changes. + 'config:search_api.index.database_search_index', // It is expected that the configuration of the view itself is // available as a cache tag, so that the caches are invalidated if the // view configuration changes. @@ -362,6 +371,10 @@ class ViewsDisplayCachingTest extends KernelTestBase { [ 'time', [ + // It is expected that the configuration of the search index is + // available as a cache tag, so that the caches are invalidated if the + // configuration of the search index changes. + 'config:search_api.index.database_search_index', // It is expected that the configuration of the view itself is // available as a cache tag, so that the caches are invalidated if the // view configuration changes. No other tags should be available.