Loading CHANGELOG.txt +2 −0 Changes for CHANGELOG.txt: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Search API 1.x, dev (xxxx-xx-xx): --------------------------------- - #3258802 by drunken monkey: Fixed errors when indexing fields with unknown types. - #3232736 by GuyPaddock, idebr, drunken monkey: Added query tags as an option to Views. - #3188138 by drunken monkey, devad: Added an "Item URL" field to Views. Loading modules/search_api_db/src/Plugin/search_api/backend/Database.php +25 −0 Changes for modules/search_api_db/src/Plugin/search_api/backend/Database.php: 25 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ namespace Drupal\search_api_db\Plugin\search_api\backend; use Drupal\Component\Plugin\Exception\PluginException; use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\Unicode; use Drupal\Core\Cache\RefinableCacheableDependencyInterface; Loading Loading @@ -920,6 +921,18 @@ class Database extends BackendPluginBase implements AutocompleteBackendInterface return ['type' => 'int', 'size' => 'tiny']; default: try { $data_type = $this->getDataTypePluginManager()->createInstance($type); if ($data_type && !$data_type->isDefault()) { $fallback_type = $data_type->getFallbackType(); if ($fallback_type != $type) { return $this->sqlType($fallback_type); } } } catch (PluginException $e) { // Ignore. } throw new SearchApiException("Unknown field type '$type'."); } } Loading Loading @@ -1540,6 +1553,18 @@ class Database extends BackendPluginBase implements AutocompleteBackendInterface return $value ? 1 : 0; default: try { $data_type = $this->getDataTypePluginManager()->createInstance($type); if ($data_type && !$data_type->isDefault()) { $fallback_type = $data_type->getFallbackType(); if ($fallback_type != $type) { return $this->convert($value, $fallback_type, $original_type, $index); } } } catch (PluginException $e) { // Ignore. } throw new SearchApiException("Unknown field type '$type'."); } } Loading modules/search_api_db/tests/src/Kernel/BackendTest.php +79 −0 Changes for modules/search_api_db/tests/src/Kernel/BackendTest.php: 79 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ namespace Drupal\Tests\search_api_db\Kernel; use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Database\Database as CoreDatabase; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface; use Drupal\search_api\Entity\Index; use Drupal\search_api\Entity\Server; Loading @@ -20,6 +21,7 @@ use Drupal\search_api_db\DatabaseCompatibility\GenericDatabase; use Drupal\search_api_db\Plugin\search_api\backend\Database; use Drupal\search_api_db\Tests\DatabaseTestsTrait; use Drupal\Tests\search_api\Kernel\BackendTestBase; use Drupal\Tests\search_api\Kernel\TestLogger; /** * Tests index and search capabilities using the Database search backend. Loading Loading @@ -50,6 +52,15 @@ class BackendTest extends BackendTestBase { */ protected $indexId = 'database_search_index'; /** * The test logger installed in the container. * * Will throw expections whenever a warning or error is logged. * * @var \Drupal\Tests\search_api\Kernel\TestLogger */ protected $logger; /** * {@inheritdoc} */ Loading Loading @@ -94,6 +105,19 @@ class BackendTest extends BackendTestBase { $index->save(); } /** * {@inheritdoc} */ public function register(ContainerBuilder $container): void { parent::register($container); // Set a logger that will throw exceptions when warnings/errors are logged. $this->logger = new TestLogger(''); $container->set('logger.factory', $this->logger); $container->set('logger.channel.search_api', $this->logger); $container->set('logger.channel.search_api_db', $this->logger); } /** * {@inheritdoc} */ Loading Loading @@ -126,6 +150,7 @@ class BackendTest extends BackendTestBase { $this->regressionTest2873023(); $this->regressionTest3199355(); $this->regressionTest3225675(); $this->regressionTest3258802(); } /** Loading Loading @@ -699,9 +724,17 @@ class BackendTest extends BackendTestBase { protected function regressionTest2925464() { $index = $this->getIndex(); // Changing the field type and, thus, column type, will cause a database // error on MySQL and Postgres due to illegal integer values. if (in_array(\Drupal::database()->driver(), ['mysql', 'pgsql'])) { $this->logger->setExpectedErrors(1); } $index->getField('category')->setType('integer'); $index->save(); $this->logger->assertAllExpectedErrorsEncountered(); $index->getField('category')->setType('string'); $index->save(); Loading Loading @@ -886,6 +919,42 @@ class BackendTest extends BackendTestBase { $this->indexItems($this->indexId); } /** * Tests whether unknown field types are handled correctly. * * @see https://www.drupal.org/node/3258802 */ protected function regressionTest3258802(): void { $this->enableModules(['search_api_test']); $index = $this->getIndex(); $type_field = $index->getField('type'); $this->assertEquals('string', $type_field->getType()); $type_field->setType('search_api_test_unsupported'); $index->save(); // No tasks should have been created. $task_manager = \Drupal::getContainer()->get('search_api.task_manager'); $this->assertEquals(0, $task_manager->getTasksCount()); // Reindexing should work fine. $index->clear(); $this->assertEquals(5, $this->indexItems($this->indexId)); $results = $index->query()->addCondition('type', 'article')->execute(); $this->assertResults([4, 5], $results, 'Search with filter on field with unknown type'); $index = $this->getIndex(); $type_field = $index->getField('type'); $this->assertEquals('search_api_test_unsupported', $type_field->getType()); $type_field->setType('string'); $index->save(); // No tasks should have been created. $tasks_count = $task_manager->getTasksCount(); $this->assertEquals(0, $tasks_count); $this->indexItems($this->indexId); $this->disableModules(['search_api_test']); } /** * {@inheritdoc} */ Loading @@ -904,6 +973,16 @@ class BackendTest extends BackendTestBase { return $index; } /** * {@inheritdoc} */ protected function regressionTest2471509(): void { // As this test will log an exception, we need to take that into account. $this->logger->setExpectedErrors(2); parent::regressionTest2471509(); $this->logger->assertAllExpectedErrorsEncountered(); } /** * {@inheritdoc} */ Loading tests/src/Kernel/TestLogger.php 0 → 100644 +96 −0 Changes for tests/src/Kernel/TestLogger.php: 96 added lines, 0 removed lines. Original line number Diff line number Diff line <?php namespace Drupal\Tests\search_api\Kernel; use Drupal\Core\Logger\LoggerChannel; use Drupal\Core\Logger\LoggerChannelFactoryInterface; use Drupal\Core\Logger\LoggerChannelInterface; use Drupal\Core\Logger\RfcLoggerTrait; use Drupal\Core\Logger\RfcLogLevel; use PHPUnit\Framework\Assert; use Psr\Log\LoggerInterface; /** * Provides a logger that throws exceptions when logging errors. */ class TestLogger extends LoggerChannel implements LoggerChannelFactoryInterface { use RfcLoggerTrait; /** * The number of currently expected errors. * * @var int */ protected $expectedErrors = 0; /** * Retrieves the number of currently expected logged errors. * * @return int * The number of currently expected logged errors. */ public function getExpectedErrors(): int { return $this->expectedErrors; } /** * Sets an expectation for one or more errors to be logged. * * @param int $num_expected_errors * The new expected number of errors. * @param int|null $expected_previous_setting * (optional) The expected previous setting of the number of expected errors * in order to make an assertion on that. Or NULL to skip the assertion. * * @return $this */ public function setExpectedErrors(int $num_expected_errors = 1, ?int $expected_previous_setting = 0): self { if ($expected_previous_setting !== NULL) { Assert::assertEquals($expected_previous_setting, $this->expectedErrors); } $this->expectedErrors = $num_expected_errors; return $this; } /** * Asserts that all expected errors were in fact encountered. * * In other words, asserts that the currently expected number of errors to be * logged is 0. * * @return $this */ public function assertAllExpectedErrorsEncountered(): self { Assert::assertEquals(0, $this->expectedErrors); return $this; } /** * {@inheritdoc} */ public function log($level, $message, array $context = []): void { if ($level < RfcLogLevel::INFO) { if ($this->expectedErrors > 0) { --$this->expectedErrors; return; } $message = strtr($message, $context); throw new \Exception($message); } } /** * {@inheritdoc} */ public function get($channel): LoggerChannelInterface { return $this; } /** * {@inheritdoc} */ public function addLogger(LoggerInterface $logger, $priority = 0): void { } } Loading
CHANGELOG.txt +2 −0 Changes for CHANGELOG.txt: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Search API 1.x, dev (xxxx-xx-xx): --------------------------------- - #3258802 by drunken monkey: Fixed errors when indexing fields with unknown types. - #3232736 by GuyPaddock, idebr, drunken monkey: Added query tags as an option to Views. - #3188138 by drunken monkey, devad: Added an "Item URL" field to Views. Loading
modules/search_api_db/src/Plugin/search_api/backend/Database.php +25 −0 Changes for modules/search_api_db/src/Plugin/search_api/backend/Database.php: 25 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ namespace Drupal\search_api_db\Plugin\search_api\backend; use Drupal\Component\Plugin\Exception\PluginException; use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\Unicode; use Drupal\Core\Cache\RefinableCacheableDependencyInterface; Loading Loading @@ -920,6 +921,18 @@ class Database extends BackendPluginBase implements AutocompleteBackendInterface return ['type' => 'int', 'size' => 'tiny']; default: try { $data_type = $this->getDataTypePluginManager()->createInstance($type); if ($data_type && !$data_type->isDefault()) { $fallback_type = $data_type->getFallbackType(); if ($fallback_type != $type) { return $this->sqlType($fallback_type); } } } catch (PluginException $e) { // Ignore. } throw new SearchApiException("Unknown field type '$type'."); } } Loading Loading @@ -1540,6 +1553,18 @@ class Database extends BackendPluginBase implements AutocompleteBackendInterface return $value ? 1 : 0; default: try { $data_type = $this->getDataTypePluginManager()->createInstance($type); if ($data_type && !$data_type->isDefault()) { $fallback_type = $data_type->getFallbackType(); if ($fallback_type != $type) { return $this->convert($value, $fallback_type, $original_type, $index); } } } catch (PluginException $e) { // Ignore. } throw new SearchApiException("Unknown field type '$type'."); } } Loading
modules/search_api_db/tests/src/Kernel/BackendTest.php +79 −0 Changes for modules/search_api_db/tests/src/Kernel/BackendTest.php: 79 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ namespace Drupal\Tests\search_api_db\Kernel; use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Database\Database as CoreDatabase; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface; use Drupal\search_api\Entity\Index; use Drupal\search_api\Entity\Server; Loading @@ -20,6 +21,7 @@ use Drupal\search_api_db\DatabaseCompatibility\GenericDatabase; use Drupal\search_api_db\Plugin\search_api\backend\Database; use Drupal\search_api_db\Tests\DatabaseTestsTrait; use Drupal\Tests\search_api\Kernel\BackendTestBase; use Drupal\Tests\search_api\Kernel\TestLogger; /** * Tests index and search capabilities using the Database search backend. Loading Loading @@ -50,6 +52,15 @@ class BackendTest extends BackendTestBase { */ protected $indexId = 'database_search_index'; /** * The test logger installed in the container. * * Will throw expections whenever a warning or error is logged. * * @var \Drupal\Tests\search_api\Kernel\TestLogger */ protected $logger; /** * {@inheritdoc} */ Loading Loading @@ -94,6 +105,19 @@ class BackendTest extends BackendTestBase { $index->save(); } /** * {@inheritdoc} */ public function register(ContainerBuilder $container): void { parent::register($container); // Set a logger that will throw exceptions when warnings/errors are logged. $this->logger = new TestLogger(''); $container->set('logger.factory', $this->logger); $container->set('logger.channel.search_api', $this->logger); $container->set('logger.channel.search_api_db', $this->logger); } /** * {@inheritdoc} */ Loading Loading @@ -126,6 +150,7 @@ class BackendTest extends BackendTestBase { $this->regressionTest2873023(); $this->regressionTest3199355(); $this->regressionTest3225675(); $this->regressionTest3258802(); } /** Loading Loading @@ -699,9 +724,17 @@ class BackendTest extends BackendTestBase { protected function regressionTest2925464() { $index = $this->getIndex(); // Changing the field type and, thus, column type, will cause a database // error on MySQL and Postgres due to illegal integer values. if (in_array(\Drupal::database()->driver(), ['mysql', 'pgsql'])) { $this->logger->setExpectedErrors(1); } $index->getField('category')->setType('integer'); $index->save(); $this->logger->assertAllExpectedErrorsEncountered(); $index->getField('category')->setType('string'); $index->save(); Loading Loading @@ -886,6 +919,42 @@ class BackendTest extends BackendTestBase { $this->indexItems($this->indexId); } /** * Tests whether unknown field types are handled correctly. * * @see https://www.drupal.org/node/3258802 */ protected function regressionTest3258802(): void { $this->enableModules(['search_api_test']); $index = $this->getIndex(); $type_field = $index->getField('type'); $this->assertEquals('string', $type_field->getType()); $type_field->setType('search_api_test_unsupported'); $index->save(); // No tasks should have been created. $task_manager = \Drupal::getContainer()->get('search_api.task_manager'); $this->assertEquals(0, $task_manager->getTasksCount()); // Reindexing should work fine. $index->clear(); $this->assertEquals(5, $this->indexItems($this->indexId)); $results = $index->query()->addCondition('type', 'article')->execute(); $this->assertResults([4, 5], $results, 'Search with filter on field with unknown type'); $index = $this->getIndex(); $type_field = $index->getField('type'); $this->assertEquals('search_api_test_unsupported', $type_field->getType()); $type_field->setType('string'); $index->save(); // No tasks should have been created. $tasks_count = $task_manager->getTasksCount(); $this->assertEquals(0, $tasks_count); $this->indexItems($this->indexId); $this->disableModules(['search_api_test']); } /** * {@inheritdoc} */ Loading @@ -904,6 +973,16 @@ class BackendTest extends BackendTestBase { return $index; } /** * {@inheritdoc} */ protected function regressionTest2471509(): void { // As this test will log an exception, we need to take that into account. $this->logger->setExpectedErrors(2); parent::regressionTest2471509(); $this->logger->assertAllExpectedErrorsEncountered(); } /** * {@inheritdoc} */ Loading
tests/src/Kernel/TestLogger.php 0 → 100644 +96 −0 Changes for tests/src/Kernel/TestLogger.php: 96 added lines, 0 removed lines. Original line number Diff line number Diff line <?php namespace Drupal\Tests\search_api\Kernel; use Drupal\Core\Logger\LoggerChannel; use Drupal\Core\Logger\LoggerChannelFactoryInterface; use Drupal\Core\Logger\LoggerChannelInterface; use Drupal\Core\Logger\RfcLoggerTrait; use Drupal\Core\Logger\RfcLogLevel; use PHPUnit\Framework\Assert; use Psr\Log\LoggerInterface; /** * Provides a logger that throws exceptions when logging errors. */ class TestLogger extends LoggerChannel implements LoggerChannelFactoryInterface { use RfcLoggerTrait; /** * The number of currently expected errors. * * @var int */ protected $expectedErrors = 0; /** * Retrieves the number of currently expected logged errors. * * @return int * The number of currently expected logged errors. */ public function getExpectedErrors(): int { return $this->expectedErrors; } /** * Sets an expectation for one or more errors to be logged. * * @param int $num_expected_errors * The new expected number of errors. * @param int|null $expected_previous_setting * (optional) The expected previous setting of the number of expected errors * in order to make an assertion on that. Or NULL to skip the assertion. * * @return $this */ public function setExpectedErrors(int $num_expected_errors = 1, ?int $expected_previous_setting = 0): self { if ($expected_previous_setting !== NULL) { Assert::assertEquals($expected_previous_setting, $this->expectedErrors); } $this->expectedErrors = $num_expected_errors; return $this; } /** * Asserts that all expected errors were in fact encountered. * * In other words, asserts that the currently expected number of errors to be * logged is 0. * * @return $this */ public function assertAllExpectedErrorsEncountered(): self { Assert::assertEquals(0, $this->expectedErrors); return $this; } /** * {@inheritdoc} */ public function log($level, $message, array $context = []): void { if ($level < RfcLogLevel::INFO) { if ($this->expectedErrors > 0) { --$this->expectedErrors; return; } $message = strtr($message, $context); throw new \Exception($message); } } /** * {@inheritdoc} */ public function get($channel): LoggerChannelInterface { return $this; } /** * {@inheritdoc} */ public function addLogger(LoggerInterface $logger, $priority = 0): void { } }