Skip to content
Snippets Groups Projects
Commit a25315c1 authored by Thomas Seidl's avatar Thomas Seidl
Browse files

Issue #3262824 by drunken monkey: Fixed handling of removed notification plugin dependencies.

parent 370fe798
No related branches found
No related tags found
No related merge requests found
Search API Saved Searches 1.x, dev (xxxx-xx-xx):
------------------------------------------------
- #3262824 by drunken monkey: Fixed handling of removed notification plugin
dependencies.
Search API Saved Searches 1.0, Beta 1 (2023-12-27):
---------------------------------------------------
......
......@@ -510,9 +510,9 @@ class SavedSearchType extends ConfigEntityBundleBase implements SavedSearchTypeI
$call_on_removal = [];
foreach ($dependencies as $dependency_type => $dependency_objects) {
// Annoyingly, modules and theme dependencies come not keyed by dependency
// name here, while entities do. Flip the array for modules and themes to
// make the code simpler.
// Annoyingly, modules and theme dependencies do not come keyed by
// dependency name here, while entities do. Flip the array for modules
// and themes to make the code simpler.
if (in_array($dependency_type, ['module', 'theme'])) {
$dependency_objects = array_flip($dependency_objects);
}
......@@ -541,7 +541,7 @@ class SavedSearchType extends ConfigEntityBundleBase implements SavedSearchTypeI
// However this plays out, it will lead to a change.
$changed = TRUE;
foreach ($dependency_sources['optional'] as $plugin_type => $plugins) {
foreach ($dependency_sources['optional'] as $plugins) {
// Type entities currently have no soft dependencies, so this has to
// be a plugin dependency. We want to call onDependencyRemoval() on
// that plugin.
......@@ -550,7 +550,7 @@ class SavedSearchType extends ConfigEntityBundleBase implements SavedSearchTypeI
$plugins = array_intersect_key($plugins, $all_plugins);
foreach ($plugins as $plugin_id => $plugin) {
$call_on_removal[$plugin_type][$plugin_id][$dependency_type][$name] = $dependency_objects[$name];
$call_on_removal[$plugin_id][$dependency_type][$name] = $dependency_objects[$name];
}
}
}
......@@ -560,19 +560,13 @@ class SavedSearchType extends ConfigEntityBundleBase implements SavedSearchTypeI
// Now for all plugins with optional dependencies (stored in
// $call_on_removal, mapped to their removed dependencies) call their
// onDependencyRemoval() methods.
$updated_config = [];
foreach ($call_on_removal as $plugin_type => $plugins) {
foreach ($plugins as $plugin_id => $plugin_dependencies) {
$removal_successful = $all_plugins[$plugin_id]->onDependencyRemoval($plugin_dependencies);
// If the plugin was successfully changed to remove the dependency,
// remember the new configuration to later set it. Otherwise, remove the
// plugin from the index so the dependency still gets removed.
if ($removal_successful) {
$updated_config[$plugin_type][$plugin_id] = $all_plugins[$plugin_id]->getConfiguration();
}
else {
unset($all_plugins[$plugin_id]);
}
foreach ($call_on_removal as $plugin_id => $plugin_dependencies) {
$plugin = $all_plugins[$plugin_id];
$removal_successful = $plugin->onDependencyRemoval($plugin_dependencies);
// If the plugin could not be changed to remove the dependency, remove it
// from the type so the dependency still gets removed.
if (!$removal_successful) {
unset($all_plugins[$plugin_id]);
}
}
......
plugin.plugin_configuration.search_api_saved_searches_notification.search_api_saved_searches_test:
type: mapping
label: Test notification plugin
mapping:
dependencies:
type: config_dependencies
label: Dependencies
name: 'Search API Saved Searches Test'
type: module
description: 'Support module for Search API Saved Searches tests'
package: Testing
dependencies:
- search_api_saved_searches:search_api_saved_searches
- search_api:search_api_test
<?php
declare(strict_types = 1);
namespace Drupal\search_api_saved_searches_test\Plugin\search_api_saved_searches\notification;
use Drupal\search_api\Query\ResultSetInterface;
use Drupal\search_api_saved_searches\Notification\NotificationPluginBase;
use Drupal\search_api_saved_searches\SavedSearchInterface;
use Drupal\search_api_test\TestPluginTrait;
/**
* Provides a notification plugin to use in tests.
*
* @SearchApiSavedSearchesNotification(
* id = "search_api_saved_searches_test",
* label = @Translation("Test plugin"),
* )
*/
class TestNotificationPlugin extends NotificationPluginBase {
use TestPluginTrait;
/**
* {@inheritdoc}
*/
public function calculateDependencies(): array {
return $this->configuration['dependencies'] ?? [];
}
/**
* {@inheritdoc}
*/
public function onDependencyRemoval(array $dependencies): bool {
$remove = $this->getReturnValue(__FUNCTION__, FALSE);
if ($remove) {
unset($this->configuration['dependencies']);
}
return $remove;
}
/**
* {@inheritdoc}
*/
public function notify(SavedSearchInterface $search, ResultSetInterface $results): void {
}
}
<?php
declare(strict_types = 1);
namespace Drupal\Tests\search_api_saved_searches\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\search_api_saved_searches\Entity\SavedSearchType;
use Drupal\search_api_test\PluginTestTrait;
/**
* Tests that removal of plugin dependencies is handled correctly.
*
* @group search_api_saved_searches
*/
class DependencyRemovalTest extends KernelTestBase {
use PluginTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'help',
'options',
'search_api_saved_searches',
'search_api_saved_searches_test',
'user',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('search_api_saved_search');
$this->installConfig('search_api_saved_searches');
$this->installSchema('user', 'users_data');
}
/**
* Tests that removal of optional dependencies is handled correctly.
*
* @param string $dependency_type
* The type of dependency to use, "module" or "config".
* @param bool $remove
* Whether the plugin should successfully adapt upon removal of the
* dependency.
*
* @dataProvider optionalDependencyRemovalTestDataProvider
*/
public function testOptionalDependencyRemoval(string $dependency_type, bool $remove): void {
if ($dependency_type === 'module') {
$dependency_key = 'module';
$dependency_name = 'help';
}
else {
assert($dependency_type === 'config');
// Use a saved search type as the dependency, since we have that available
// anyways. The entity type should not matter at all, though.
$dependency = SavedSearchType::create([
'id' => 'dependency',
'name' => 'Test dependency',
'backend' => 'search_api_test',
]);
$dependency->save();
$dependency_key = $dependency->getConfigDependencyKey();
$dependency_name = $dependency->getConfigDependencyName();
}
$type = SavedSearchType::load('default');
$plugins = \Drupal::getContainer()
->get('plugin.manager.search_api_saved_searches.notification')
->createPlugins($type, ['search_api_saved_searches_test'], [
'search_api_saved_searches_test' => [
'dependencies' => [
$dependency_key => [
$dependency_name,
],
],
],
]);
$type->setNotificationPlugins($plugins);
$type->save();
// Check that the dependencies were calculated correctly.
$type_dependencies = $type->getDependencies();
$this->assertContains($dependency_name, $type_dependencies[$dependency_key]);
// Tell the backend plugin whether it should successfully remove the
// dependency.
$this->setReturnValue('notification', 'onDependencyRemoval', $remove);
// Now remove the dependency.
if ($dependency_type === 'module') {
\Drupal::getContainer()->get('module_installer')->uninstall(['help']);
}
else {
if (empty($dependency)) {
throw new \RuntimeException('$dependency not set.');
}
$dependency->delete();
}
// Reload the type.
$storage = \Drupal::entityTypeManager()->getStorage($type->getEntityTypeId());
$storage->resetCache();
$type = $storage->load($type->id());
$this->assertInstanceOf(SavedSearchType::class, $type);
$plugins = $type->getNotificationPlugins();
if ($remove) {
$this->assertArrayHasKey('search_api_saved_searches_test', $plugins);
$this->assertArrayNotHasKey('dependencies', $plugins['search_api_saved_searches_test']->getConfiguration());
}
else {
$this->assertArrayNotHasKey('search_api_saved_searches_test', $plugins);
}
}
/**
* Provides test data sets for testOptionalDependencyRemoval().
*
* @return array[]
* An associative array of argument arrays for
* testOptionalDependencyRemoval(), keyed by data set labels.
*
* @see testOptionalDependencyRemoval()
*/
public function optionalDependencyRemovalTestDataProvider(): array {
return [
['module', FALSE],
['module', TRUE],
['config', FALSE],
['config', TRUE],
];
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment