From 5043cdd839184d6fbd724f953cf7327ec4e90274 Mon Sep 17 00:00:00 2001 From: Markus Friedrich Date: Fri, 2 Jun 2023 10:28:42 +0200 Subject: [PATCH] [!!!][TASK] Remove deprecated Node class Removes deprecated class ApacheSolrForTypo3\Solr\System\Solr\Node and it's usages. - Replace deprecated Node class with Endpoint - Replace getUsername/getPassword with getAuthentication() - Change SolrConnection->getNode() to SolrConnection->getEndpoint Resolves: #3630, #3043 Replaces: #3518 --- Classes/ConnectionManager.php | 41 ++++- .../Exception/InvalidConnectionException.php | 25 +++ .../IndexQueue/FrontendHelper/PageIndexer.php | 2 +- Classes/Report/SolrStatus.php | 2 +- Classes/System/Solr/Node.php | 144 ------------------ Classes/System/Solr/SolrConnection.php | 28 ++-- Classes/System/Util/SiteUtility.php | 12 +- .../SiteConfiguration/Overrides/sites.php | 2 +- Documentation/Backend/ConnectionManager.rst | 6 +- Documentation/FAQ/Index.rst | 35 +---- Documentation/Frontend/Languages.rst | 25 +-- .../Site_handling_Setup_solr_01.png | Bin 41425 -> 63737 bytes Documentation/Releases/solr-release-12-0.rst | 16 +- Tests/Integration/ConnectionManagerTest.php | 4 +- ...tom_translated_record_with_mm_relation.csv | 7 +- ...ated_record_with_mm_relation_to_a_page.csv | 7 +- Tests/Integration/IntegrationTest.php | 2 +- Tests/Unit/ConnectionManagerTest.php | 118 ++++++++------ Tests/Unit/System/Solr/SolrConnectionTest.php | 85 ++++++----- 19 files changed, 220 insertions(+), 341 deletions(-) create mode 100644 Classes/Exception/InvalidConnectionException.php delete mode 100644 Classes/System/Solr/Node.php diff --git a/Classes/ConnectionManager.php b/Classes/ConnectionManager.php index d9e8d2ad99..094077fbf3 100644 --- a/Classes/ConnectionManager.php +++ b/Classes/ConnectionManager.php @@ -21,13 +21,15 @@ use ApacheSolrForTypo3\Solr\Domain\Site\Site; use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository; use ApacheSolrForTypo3\Solr\Exception\InvalidArgumentException; +use ApacheSolrForTypo3\Solr\Exception\InvalidConnectionException; use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository as PagesRepositoryAtExtSolr; -use ApacheSolrForTypo3\Solr\System\Solr\Node; use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection; use ApacheSolrForTypo3\Solr\System\Util\SiteUtility; use Doctrine\DBAL\Exception as DBALException; +use Solarium\Core\Client\Endpoint; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Site\Entity\Site as Typo3Site; + use TYPO3\CMS\Core\Utility\GeneralUtility; use function json_encode; @@ -57,25 +59,48 @@ public function __construct( } /** - * Creates a solr connection for read and write endpoints + * Creates a Solr connection for read and write endpoints + * + * @throw InvalidConnectionException */ - public function getSolrConnectionForNodes(array $readNodeConfiguration, array $writeNodeConfiguration): SolrConnection + public function getSolrConnectionForEndpoints(array $readEndpointConfiguration, array $writeEndpointConfiguration): SolrConnection { - $connectionHash = md5(json_encode($readNodeConfiguration) . json_encode($writeNodeConfiguration)); + $connectionHash = md5(json_encode($readEndpointConfiguration) . json_encode($writeEndpointConfiguration)); if (!isset(self::$connections[$connectionHash])) { - $readNode = Node::fromArray($readNodeConfiguration); - $writeNode = Node::fromArray($writeNodeConfiguration); - self::$connections[$connectionHash] = GeneralUtility::makeInstance(SolrConnection::class, $readNode, $writeNode); + $readEndpoint = new Endpoint($readEndpointConfiguration); + if (!$this->isValidEndpoint($readEndpoint)) { + throw new InvalidConnectionException('Invalid read endpoint'); + } + + $writeEndpoint = new Endpoint($writeEndpointConfiguration); + if (!$this->isValidEndpoint($writeEndpoint)) { + throw new InvalidConnectionException('Invalid write endpoint'); + } + + self::$connections[$connectionHash] = GeneralUtility::makeInstance(SolrConnection::class, $readEndpoint, $writeEndpoint); } + return self::$connections[$connectionHash]; } + /** + * Checks if endpoint is valid + */ + protected function isValidEndpoint(Endpoint $endpoint): bool + { + return + !empty($endpoint->getHost()) + && !empty($endpoint->getPort()) + && !empty($endpoint->getCore()) + ; + } + /** * Creates a solr configuration from the configuration array and returns it. */ public function getConnectionFromConfiguration(array $solrConfiguration): SolrConnection { - return $this->getSolrConnectionForNodes($solrConfiguration['read'], $solrConfiguration['write']); + return $this->getSolrConnectionForEndpoints($solrConfiguration['read'], $solrConfiguration['write']); } /** diff --git a/Classes/Exception/InvalidConnectionException.php b/Classes/Exception/InvalidConnectionException.php new file mode 100644 index 0000000000..290a18e6af --- /dev/null +++ b/Classes/Exception/InvalidConnectionException.php @@ -0,0 +1,25 @@ +responseData['solrConnection'] = [ 'rootPage' => $indexQueueItem->getRootPageUid(), 'sys_language_uid' => $tsfe->getLanguage()->getLanguageId(), - 'solr' => (string)$this->solrConnection->getNode('write'), + 'solr' => $this->solrConnection->getEndpoint('write')->getCoreBaseUri(), ]; foreach ($this->documentsSentToSolr as $document) { diff --git a/Classes/Report/SolrStatus.php b/Classes/Report/SolrStatus.php index f278f70f18..fc71bcaf92 100644 --- a/Classes/Report/SolrStatus.php +++ b/Classes/Report/SolrStatus.php @@ -113,7 +113,7 @@ protected function getConnectionStatus(Site $site, array $solrConnection): Statu $this->responseStatus = ContextualFeedbackSeverity::OK; $solrAdmin = $this->connectionManager - ->getSolrConnectionForNodes($solrConnection['read'], $solrConnection['write']) + ->getSolrConnectionForEndpoints($solrConnection['read'], $solrConnection['write']) ->getAdminService(); $solrVersion = $this->checkSolrVersion($solrAdmin); diff --git a/Classes/System/Solr/Node.php b/Classes/System/Solr/Node.php deleted file mode 100644 index e1b039e73f..0000000000 --- a/Classes/System/Solr/Node.php +++ /dev/null @@ -1,144 +0,0 @@ - - * @copyright Copyright (c) 2009-2020 Timo Hund - * - * @deprecated Class will be removed with Ext:solr 12.x. Use class \Solarium\Core\Client\Endpoint instead. - */ -class Node extends Endpoint -{ - /** - * Node constructor. - */ - public function __construct( - string $scheme = 'http', - string $host = 'localhost', - int $port = 8983, - string $path = '/solr/core_en/', - ?string $username = null, - ?string $password = null, - ) { - $elements = explode('/', trim($path, '/')); - $coreName = (string)array_pop($elements); - // Remove API version - array_pop($elements); - - // The path should always have the same format! - $path = trim(implode('/', $elements), '/'); - - $options = [ - 'scheme' => $scheme, - 'host' => $host, - 'port' => $port, - 'path' => '/' . $path, - 'collection' => null, - 'core' => $coreName, - 'leader' => false, - ]; - - parent::__construct($options); - $this->setAuthentication($username, $password); - } - - public static function fromArray(array $configuration): Node - { - static::checkIfRequiredKeyIsSet($configuration, 'scheme'); - static::checkIfRequiredKeyIsSet($configuration, 'host'); - static::checkIfRequiredKeyIsSet($configuration, 'port'); - static::checkIfRequiredKeyIsSet($configuration, 'path'); - - $scheme = $configuration['scheme']; - $host = $configuration['host']; - $port = (int)$configuration['port']; - $path = $configuration['path']; - - $username = $configuration['username'] ?? ''; - $password = $configuration['password'] ?? ''; - return new Node($scheme, $host, $port, $path, $username, $password); - } - - /** - * Checks if the required configuration option is set. - * - * @throws UnexpectedValueException - */ - protected static function checkIfRequiredKeyIsSet(array $configuration, string $name): void - { - if (empty($configuration[$name])) { - throw new UnexpectedValueException('Required solr connection property ' . $name . ' is missing.'); - } - } - - public function getUsername(): string - { - return (string)$this->getOption('username'); - } - - public function getPassword(): string - { - return (string)$this->getOption('password'); - } - - /** - * Returns the path including api path. - */ - public function getCoreBasePath(): string - { - $pathWithoutLeadingAndTrailingSlashes = trim(trim($this->getPath()), '/'); - $pathWithoutLastSegment = substr($pathWithoutLeadingAndTrailingSlashes, 0, strrpos($pathWithoutLeadingAndTrailingSlashes, '/')); - return ($pathWithoutLastSegment === '') ? '/' : '/' . $pathWithoutLastSegment . '/'; - } - - /** - * Returns the core name from the configured path. - * - * @deprecated Will be removed with Ext:solr 12.x. Use method getCore() instead. - */ - public function getCoreName(): string - { - return $this->getCore(); - } - - public function getSolariumClientOptions(): array - { - return [ - 'host' => $this->getHost(), - 'port' => $this->getPort(), - 'scheme' => $this->getScheme(), - 'path' => $this->getPath(), - 'core' => $this->getCore(), - ]; - } - - /** - * @deprecated Will be removed with Ext:solr 12.x. Use methods getCoreBaseUri() for API version 1 instead - */ - public function __toString(): string - { - return $this->getCoreBaseUri(); - } -} diff --git a/Classes/System/Solr/SolrConnection.php b/Classes/System/Solr/SolrConnection.php index ed9147bde5..3cfd48bc38 100644 --- a/Classes/System/Solr/SolrConnection.php +++ b/Classes/System/Solr/SolrConnection.php @@ -34,6 +34,7 @@ use Psr\Http\Message\StreamFactoryInterface; use Solarium\Client; use Solarium\Core\Client\Adapter\Psr18Adapter; +use Solarium\Core\Client\Endpoint; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -58,9 +59,9 @@ class SolrConnection protected ?SchemaParser $schemaParser = null; /** - * @var Node[] + * @var Endpoint[] */ - protected array $nodes = []; + protected array $endpoints = []; protected ?SolrLogManager $logger = null; @@ -83,8 +84,8 @@ class SolrConnection * @throws NotFoundExceptionInterface */ public function __construct( - Node $readNode, - Node $writeNode, + Endpoint $readEndpoint, + Endpoint $writeEndpoint, TypoScriptConfiguration $configuration = null, SynonymParser $synonymParser = null, StopWordParser $stopWordParser = null, @@ -95,9 +96,9 @@ public function __construct( StreamFactoryInterface $streamFactory = null, EventDispatcherInterface $eventDispatcher = null, ) { - $this->nodes['read'] = $readNode; - $this->nodes['write'] = $writeNode; - $this->nodes['admin'] = $writeNode; + $this->endpoints['read'] = $readEndpoint; + $this->endpoints['write'] = $writeEndpoint; + $this->endpoints['admin'] = $writeEndpoint; $this->configuration = $configuration ?? Util::getSolrConfiguration(); $this->synonymParser = $synonymParser; $this->stopWordParser = $stopWordParser; @@ -112,9 +113,9 @@ public function __construct( /** * Returns Endpoint by key */ - public function getNode(string $key): Node + public function getEndpoint(string $key): Endpoint { - return $this->nodes[$key]; + return $this->endpoints[$key]; } /** @@ -200,13 +201,12 @@ protected function buildWriteService(): SolrWriteService */ protected function initializeClient(Client $client, string $endpointKey): Client { - if (trim($this->getNode($endpointKey)->getUsername()) === '') { + $authentication = $this->getEndpoint($endpointKey)->getAuthentication(); + if (trim($authentication['username'] ?? '') === '') { return $client; } - $username = $this->getNode($endpointKey)->getUsername(); - $password = $this->getNode($endpointKey)->getPassword(); - $this->setAuthenticationOnAllEndpoints($client, $username, $password); + $this->setAuthenticationOnAllEndpoints($client, $authentication['username'], $authentication['password']); return $client; } @@ -236,7 +236,7 @@ protected function getClient(string $endpointKey): Client $client->getPlugin('postbigrequest'); $client->clearEndpoints(); - $newEndpointOptions = $this->getNode($endpointKey)->getSolariumClientOptions(); + $newEndpointOptions = $this->getEndpoint($endpointKey)->getOptions(); $newEndpointOptions['key'] = $endpointKey; $client->createEndpoint($newEndpointOptions, true); diff --git a/Classes/System/Util/SiteUtility.php b/Classes/System/Util/SiteUtility.php index 3c7df7bcc1..41a588a966 100644 --- a/Classes/System/Util/SiteUtility.php +++ b/Classes/System/Util/SiteUtility.php @@ -104,10 +104,8 @@ public static function getSolrConnectionConfiguration( 'scheme' => self::getConnectionProperty($typo3Site, 'scheme', $languageUid, 'read', 'http'), 'host' => self::getConnectionProperty($typo3Site, 'host', $languageUid, 'read', 'localhost'), 'port' => (int)self::getConnectionProperty($typo3Site, 'port', $languageUid, 'read', 8983), - // @todo: transform core to path - 'path' => - self::getConnectionProperty($typo3Site, 'path', $languageUid, 'read', '/solr/') . - $solrReadCore . '/' , + 'path' => self::getConnectionProperty($typo3Site, 'path', $languageUid, 'read', ''), + 'core' => $solrReadCore, 'username' => self::getConnectionProperty($typo3Site, 'username', $languageUid, 'read', ''), 'password' => self::getConnectionProperty($typo3Site, 'password', $languageUid, 'read', ''), ], @@ -115,10 +113,8 @@ public static function getSolrConnectionConfiguration( 'scheme' => self::getConnectionProperty($typo3Site, 'scheme', $languageUid, 'write', 'http'), 'host' => self::getConnectionProperty($typo3Site, 'host', $languageUid, 'write', 'localhost'), 'port' => (int)self::getConnectionProperty($typo3Site, 'port', $languageUid, 'write', 8983), - // @todo: transform core to path - 'path' => - self::getConnectionProperty($typo3Site, 'path', $languageUid, 'write', '/solr/') . - $solrWriteCore . '/' , + 'path' => self::getConnectionProperty($typo3Site, 'path', $languageUid, 'write', ''), + 'core' => $solrWriteCore, 'username' => self::getConnectionProperty($typo3Site, 'username', $languageUid, 'write', ''), 'password' => self::getConnectionProperty($typo3Site, 'password', $languageUid, 'write', ''), ], diff --git a/Configuration/SiteConfiguration/Overrides/sites.php b/Configuration/SiteConfiguration/Overrides/sites.php index e0682844e1..9588ca3781 100644 --- a/Configuration/SiteConfiguration/Overrides/sites.php +++ b/Configuration/SiteConfiguration/Overrides/sites.php @@ -60,7 +60,7 @@ $GLOBALS['SiteConfiguration']['site']['columns']['solr_path_read'] = [ 'label' => 'URL path to Apache Solr server', - 'description' => 'I.e. if you use Hosted-Solr.com the path inside the admin panel. Should not contain "/solr/".', + 'description' => 'Must not contain "/solr/"! Unlesss you have an additional "solr" segment in your path like "http://localhost:8983/solr/solr/core_en".', 'config' => [ 'type' => 'input', 'eval' => 'trim', diff --git a/Documentation/Backend/ConnectionManager.rst b/Documentation/Backend/ConnectionManager.rst index 3e59fe2f1d..fa076e2f20 100644 --- a/Documentation/Backend/ConnectionManager.rst +++ b/Documentation/Backend/ConnectionManager.rst @@ -49,7 +49,7 @@ Example: hreflang: en-US direction: '' flag: global - solr_host_read: solr_node_1 + solr_host_read: solr-host-1 solr_core_read: core_en languageId: '0' - @@ -63,13 +63,13 @@ Example: hreflang: de-DE direction: '' flag: global - solr_host_read: solr_node_2 + solr_host_read: solr-host-2 solr_core_read: core_de languageId: '1' rootPageId: 3 routes: { } solr_enabled_read: true - solr_path_read: /solr/ + solr_path_read: / solr_port_read: 8983 solr_scheme_read: http solr_use_write_connection: false diff --git a/Documentation/FAQ/Index.rst b/Documentation/FAQ/Index.rst index 6e9b337db0..47fab5b258 100644 --- a/Documentation/FAQ/Index.rst +++ b/Documentation/FAQ/Index.rst @@ -97,39 +97,6 @@ Did you configure the search markers ( "" and "