From 21bec230adbdc935d68cd674d3c9e1b90096a11d Mon Sep 17 00:00:00 2001 From: thomascorthals Date: Fri, 6 Mar 2026 11:06:30 +0100 Subject: [PATCH] Solr 10 support --- .github/workflows/run-tests.yml | 10 +- CHANGELOG.md | 16 +- docs/getting-started.md | 5 + .../managed-stopwords.md | 18 +++ .../managed-synonyms.md | 18 +++ examples/execute_all.php | 23 ++- .../ManagedResources/Query/AbstractQuery.php | 28 ++++ .../RequestBuilder/Resource.php | 49 ++++-- .../AbstractTechproductsTestCase.php | 149 ++++++------------ .../Integration/Fixtures/conf/schema10.patch | 22 +++ .../Fixtures/conf/solrconf10.patch | 53 +++++++ .../docker/solr10_cloud/docker-compose.yml | 111 +++++++++++++ .../docker/solr10_server/docker-compose.yml | 31 ++++ .../ManagedResources/Query/StopwordsTest.php | 7 + .../ManagedResources/Query/SynonymsTest.php | 7 + .../RequestBuilder/StopwordsTest.php | 24 ++- .../RequestBuilder/SynonymsTest.php | 24 ++- 17 files changed, 452 insertions(+), 143 deletions(-) create mode 100644 tests/Integration/Fixtures/conf/schema10.patch create mode 100644 tests/Integration/Fixtures/conf/solrconf10.patch create mode 100644 tests/Integration/Fixtures/docker/solr10_cloud/docker-compose.yml create mode 100644 tests/Integration/Fixtures/docker/solr10_server/docker-compose.yml diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ea76e232b..394a3a34b 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: php: [8.1, 8.2, 8.3, 8.4, 8.5] - solr: [7, 8, 9] + solr: [7, 8, 9, 10] mode: [cloud, server] name: PHP ${{ matrix.php }}, Solr ${{ matrix.solr }} ${{ matrix.mode }} @@ -59,6 +59,14 @@ jobs: ref: branch_9_10 path: lucene-solr + - name: Checkout solr 10.0 + if: matrix.solr == 10 + uses: actions/checkout@v4 + with: + repository: apache/solr + ref: branch_10_0 + path: lucene-solr + - name: Start Solr ${{ matrix.solr }} in ${{ matrix.mode }} mode run: | chmod -R a+w lucene-solr diff --git a/CHANGELOG.md b/CHANGELOG.md index 0955e125b..e290b2818 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [7.0.0] ### Added +- Solr 10 support - PHP 8.5 support - Solarium\QueryType\Extract\Query::setStreamType() @@ -15,13 +16,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Solarium\Plugin\MinimumScoreFilter\Document::__call() unpacks the $arguments array when forwarding a method call to the original document ### Changed - - Added `void` return type to `Solarium\Core\Plugin\PluginInterface::initPlugin()` method signature - - Added `void` return type to `Solarium\Core\Plugin\PluginInterface::deinitPlugin()` method signature - - Added `void` return type to `Solarium\Core\Plugin\AbstractPlugin::initPluginType()` method signature - - Changed return type of some Facets methods from `self`to `static` - - Replaced class property type hints with type declarations - - Added union type declarations to method signatures - - Solarium\Core\Query\Helper::formatDate() throws a `TypeError` instead of returning `false` if called with an incompatibly typed parameter +- Added `void` return type to `Solarium\Core\Plugin\PluginInterface::initPlugin()` method signature +- Added `void` return type to `Solarium\Core\Plugin\PluginInterface::deinitPlugin()` method signature +- Added `void` return type to `Solarium\Core\Plugin\AbstractPlugin::initPluginType()` method signature +- Changed return type of some Facets methods from `self` to `static` +- Replaced class property type hints with type declarations +- Added union type declarations to method signatures +- Solarium\Core\Query\Helper::formatDate() throws a `TypeError` instead of returning `false` if called with an incompatibly typed parameter +- Managed resources queries no longer work around SOLR-6853 by default. Set the 'useDoubleEncoding' option to `true` if this bug affects you. ### Removed - Solarium\Component\Result\Stats\FacetValue::getFacets(), always returned `null` diff --git a/docs/getting-started.md b/docs/getting-started.md index db472b27b..dcc95b330 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -112,6 +112,11 @@ do have one it must be contravariant. Code that calls a method with parameters of an incompatible type will result in a `TypeError` where they were previously coerced into a compatible type by PHP's type juggling. +Solarium 7 no longer works around [SOLR-6853](https://issues.apache.org/jira/browse/SOLR-6853) for +managed resources queries by default. The workaround is no longer necessary for Solr 10, but also +not forward compatible with this version. If this still affects you, set the 'useDoubleEncoding' +option to `true` on `ManagedResources\Query\Stopwords` or `ManagedResources\Query\Synonyms`. + ### Pitfall when upgrading to 6.3.6 Using a config object is no longer supported. You have to convert it to an array before passing diff --git a/docs/queries/managedresources-query/managed-stopwords.md b/docs/queries/managedresources-query/managed-stopwords.md index bc7280fd0..29fe63cd0 100644 --- a/docs/queries/managedresources-query/managed-stopwords.md +++ b/docs/queries/managedresources-query/managed-stopwords.md @@ -245,6 +245,24 @@ htmlFooter(); ``` +A note on percent-encoding reserved characters +---------------------------------------------- + +If the name of a stopword list or a stopword itself contains characters that are not +[unreserved characters as defined by RFC 3986](https://www.rfc-editor.org/rfc/rfc3986#section-2.3), +they must be percent-encoded when appearing as part of a URL. Solarium handles this for you. + +However, if you're using a Solr version prior to Solr 10 and list names or stopwords +that contain [reserved characters](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2), +you will be affected by [SOLR-6853](https://issues.apache.org/jira/browse/SOLR-6853). You can +instruct Solarium to double up on the percent-encoding as a workaround. + +```php +$query = $client->createManagedStopwords(['useDoubleEncoding' => true]); +``` + +Keep in mind that Solr may not be able to handle some of these reserved characters regardless. + A note on `HEAD` requests ------------------------- diff --git a/docs/queries/managedresources-query/managed-synonyms.md b/docs/queries/managedresources-query/managed-synonyms.md index d69b36265..19217a88a 100644 --- a/docs/queries/managedresources-query/managed-synonyms.md +++ b/docs/queries/managedresources-query/managed-synonyms.md @@ -305,6 +305,24 @@ htmlFooter(); ``` +A note on percent-encoding reserved characters +---------------------------------------------- + +If the name of a synonym map or a synonym itself contains characters that are not +[unreserved characters as defined by RFC 3986](https://www.rfc-editor.org/rfc/rfc3986#section-2.3), +they must be percent-encoded when appearing as part of a URL. Solarium handles this for you. + +However, if you're using a Solr version prior to Solr 10 and map names or synonyms +that contain [reserved characters](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2), +you will be affected by [SOLR-6853](https://issues.apache.org/jira/browse/SOLR-6853). You can +instruct Solarium to double up on the percent-encoding as a workaround. + +```php +$query = $client->createManagedSynonyms(['useDoubleEncoding' => true]); +``` + +Keep in mind that Solr may not be able to handle some of these reserved characters regardless. + A note on `HEAD` requests ------------------------- diff --git a/examples/execute_all.php b/examples/execute_all.php index e3f8b22af..aee13d9a1 100644 --- a/examples/execute_all.php +++ b/examples/execute_all.php @@ -62,8 +62,8 @@ $response = $client->coreAdmin($coreAdminQuery); } - // @todo Figure out why this fails on Solr 9. - if (9 !== $solrVersion) { + // @todo Figure out why this fails starting with Solr 9. + if (9 > $solrVersion) { // check if /mlt handler exists (it will in the github worklow, but not when running this script on its own) $query = $client->createApi([ 'version' => Request::API_V1, @@ -90,6 +90,23 @@ } } + // Solr 10 no longer comes with LocalTikaExtractionBackend, the github workflow runs a remote Tika Server instead + if (10 <= $solrVersion) { + $query = $client->createApi([ + 'version' => Request::API_V1, + 'handler' => $collection_or_core_name.'/config', + 'method' => Request::METHOD_POST, + 'rawdata' => json_encode([ + 'update-requesthandler' => [ + 'name' => '/update/extract', + 'class' => 'solr.extraction.ExtractingRequestHandler', + 'tikaserver.url' => 'http://tika:9998', + ], + ]), + ]); + $client->execute($query); + } + // check if attr_* dynamic field definition exists (it was removed from the techproducts configset in Solr 9.1) try { $query = $client->createApi([ @@ -172,7 +189,7 @@ // examples that can't be run against this Solr version $skipForSolrVersion = []; - if (9 === $solrVersion) { + if (9 <= $solrVersion) { $skipForSolrVersion[] = '2.3.1-mlt-query.php'; $skipForSolrVersion[] = '2.3.2-mlt-stream.php'; } diff --git a/src/QueryType/ManagedResources/Query/AbstractQuery.php b/src/QueryType/ManagedResources/Query/AbstractQuery.php index 8c19295ad..be0705ddd 100644 --- a/src/QueryType/ManagedResources/Query/AbstractQuery.php +++ b/src/QueryType/ManagedResources/Query/AbstractQuery.php @@ -230,4 +230,32 @@ public function removeCommand(): self * @return InitArgsInterface */ abstract public function createInitArgs(?array $initArgs = null): InitArgsInterface; + + /** + * Percent-encode names and terms twice as a workaround for SOLR-6853? + * + * @return bool + */ + public function getUseDoubleEncoding(): bool + { + return $this->getOption('useDoubleEncoding') ?? false; + } + + /** + * Percent-encode names and terms twice as a workaround for SOLR-6853? + * + * Solr versions prior to 10 required reserved characters to be doubly + * percent-encoded. Set this to true if your Solr version is affected by + * {@link https://issues.apache.org/jira/browse/SOLR-6853 SOLR-6853}. + * + * @param bool $useDoubleEncoding + * + * @return self Provides fluent interface + */ + public function setUseDoubleEncoding(bool $useDoubleEncoding): self + { + $this->setOption('useDoubleEncoding', $useDoubleEncoding); + + return $this; + } } diff --git a/src/QueryType/ManagedResources/RequestBuilder/Resource.php b/src/QueryType/ManagedResources/RequestBuilder/Resource.php index ab5250635..36f8315dd 100644 --- a/src/QueryType/ManagedResources/RequestBuilder/Resource.php +++ b/src/QueryType/ManagedResources/RequestBuilder/Resource.php @@ -13,7 +13,6 @@ use Solarium\Core\Query\AbstractRequestBuilder; use Solarium\Core\Query\QueryInterface; use Solarium\Exception\RuntimeException; -use Solarium\QueryType\ManagedResources\Query\AbstractCommand; use Solarium\QueryType\ManagedResources\Query\AbstractQuery as BaseQuery; /** @@ -36,19 +35,28 @@ public function build(QueryInterface|BaseQuery $query): Request throw new RuntimeException('Name of the resource is not set in the query.'); } + $name = rawurlencode($query->getName()); + if ($query->getUseDoubleEncoding()) { + $name = rawurlencode($name); + } + $request = parent::build($query); - // reserved characters in a REST resource name need to be encoded twice to make it through the servlet (SOLR-6853) - $request->setHandler($query->getHandler().rawurlencode(rawurlencode($query->getName()))); + $request->setHandler($query->getHandler().$name); + if (null !== $query->getCommand()) { $request->setContentType(Request::CONTENT_TYPE_APPLICATION_JSON); - $this->buildCommand($request, $query->getCommand()); + $this->buildCommand($query, $request); } else { // Lists one or all items. $request->setMethod(Request::METHOD_GET); if (null !== $term = $query->getTerm()) { - // reserved characters in a REST resource name need to be encoded twice to make it through the servlet (SOLR-6853) - $request->setHandler($request->getHandler().'/'.rawurlencode(rawurlencode($term))); + $term = rawurlencode($term); + if ($query->getUseDoubleEncoding()) { + $term = rawurlencode($term); + } + + $request->setHandler($request->getHandler().'/'.$term); } } @@ -56,15 +64,17 @@ public function build(QueryInterface|BaseQuery $query): Request } /** - * @param Request $request - * @param AbstractCommand $command + * @param QueryInterface|BaseQuery $query + * @param Request $request * * @throws RuntimeException * * @return self Provides fluent interface */ - protected function buildCommand(Request $request, AbstractCommand $command): self + protected function buildCommand(QueryInterface|BaseQuery $query, Request $request): self { + $command = $query->getCommand(); + $request->setMethod($command->getRequestMethod()); switch ($command->getType()) { @@ -72,32 +82,45 @@ protected function buildCommand(Request $request, AbstractCommand $command): sel if (null === $rawData = $command->getRawData()) { throw new RuntimeException('Missing data for ADD command.'); } + $request->setRawData($rawData); break; case BaseQuery::COMMAND_CONFIG: if (null === $rawData = $command->getRawData()) { throw new RuntimeException('Missing initArgs for CONFIG command.'); } + $request->setRawData($rawData); break; case BaseQuery::COMMAND_CREATE: if (null === $rawData = $command->getRawData()) { throw new RuntimeException('Missing class for CREATE command.'); } + $request->setRawData($rawData); break; case BaseQuery::COMMAND_DELETE: if (null === $term = $command->getTerm()) { throw new RuntimeException('Missing term for DELETE command.'); } - // reserved characters in a REST resource name need to be encoded twice to make it through the servlet (SOLR-6853) - $request->setHandler($request->getHandler().'/'.rawurlencode(rawurlencode($command->getTerm()))); + + $term = rawurlencode($term); + if ($query->getUseDoubleEncoding()) { + $term = rawurlencode($term); + } + + $request->setHandler($request->getHandler().'/'.$term); break; case BaseQuery::COMMAND_EXISTS: if (null !== $term = $command->getTerm()) { - // reserved characters in a REST resource name need to be encoded twice to make it through the servlet (SOLR-6853) - $request->setHandler($request->getHandler().'/'.rawurlencode(rawurlencode($command->getTerm()))); + $term = rawurlencode($term); + if ($query->getUseDoubleEncoding()) { + $term = rawurlencode($term); + } + + $request->setHandler($request->getHandler().'/'.$term); } + break; case BaseQuery::COMMAND_REMOVE: break; diff --git a/tests/Integration/AbstractTechproductsTestCase.php b/tests/Integration/AbstractTechproductsTestCase.php index 202b7cf14..945b27bdb 100644 --- a/tests/Integration/AbstractTechproductsTestCase.php +++ b/tests/Integration/AbstractTechproductsTestCase.php @@ -23,7 +23,6 @@ use Solarium\Core\Query\AbstractDocument; use Solarium\Core\Query\AbstractQuery; use Solarium\Core\Query\Helper; -use Solarium\Core\Query\QueryInterface; use Solarium\Core\Query\RequestBuilderInterface; use Solarium\Core\Query\Status4xxNoExceptionInterface; use Solarium\Exception\HttpException; @@ -55,11 +54,7 @@ use Solarium\QueryType\Luke\Result\Fields\FieldInfo as LukeFieldInfo; use Solarium\QueryType\Luke\Result\Index\Index as LukeIndexResult; use Solarium\QueryType\Luke\Result\Schema\Schema as LukeSchemaResult; -use Solarium\QueryType\ManagedResources\Query\AbstractQuery as AbstractManagedResourcesQuery; -use Solarium\QueryType\ManagedResources\Query\Stopwords as StopwordsQuery; -use Solarium\QueryType\ManagedResources\Query\Synonyms as SynonymsQuery; use Solarium\QueryType\ManagedResources\Query\Synonyms\Synonyms; -use Solarium\QueryType\ManagedResources\RequestBuilder\Resource as ResourceRequestBuilder; use Solarium\QueryType\ManagedResources\Result\Resources\Resource as ResourceResultItem; use Solarium\QueryType\ManagedResources\Result\Synonyms\Synonyms as SynonymsResultItem; use Solarium\QueryType\Select\Query\Query as SelectQuery; @@ -207,7 +202,8 @@ public static function responseWriterProvider(): array { return [ [AbstractQuery::WT_JSON], - [AbstractQuery::WT_PHPS], + // no longer supported in Solr 10 + // [AbstractQuery::WT_PHPS], ]; } @@ -4482,6 +4478,7 @@ public function testExtractIntoDocument(bool $usePostBigExtractRequestPlugin): v // add HTML document $extract->setFile(__DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'testhtml.html'); + $extract->addParam('capture', 'title'); $doc = $extract->createDocument(); $doc->id = 'extract-test-2-html'; $doc->cat = ['extract-test']; @@ -4505,6 +4502,7 @@ public function testExtractIntoDocument(bool $usePostBigExtractRequestPlugin): v $file = fopen('php://memory', 'w+'); fwrite($file, $contents); $extract->setFile($file); + $extract->addParam('capture', 'title'); $doc = $extract->createDocument(); $doc->id = 'extract-test-3-stream'; $doc->cat = ['extract-test']; @@ -4527,13 +4525,13 @@ public function testExtractIntoDocument(bool $usePostBigExtractRequestPlugin): v $this->assertSame(['bar 1'], $document['attr_foo_1']); $iterator->next(); $document = $iterator->current(); - $this->assertSame('HTML Test Title', $document['title'][0], 'Written document does not contain extracted title'); - $this->assertMatchesRegularExpression('/^HTML Test Title\s+HTML Test Body$/', trim($document['content'][0]), 'Written document does not contain extracted result'); + $this->assertSame('HTML Test Title', trim($document['title'][0]), 'Written document does not contain extracted title'); + $this->assertStringContainsString('HTML Test Body', $document['content'][0], 'Written document does not contain extracted result'); $this->assertSame(['bar 2'], $document['attr_foo_2']); $iterator->next(); $document = $iterator->current(); - $this->assertSame('HTML Stream Title', $document['title'][0], 'Written document does not contain extracted title'); - $this->assertMatchesRegularExpression('/^HTML Stream Title\s+HTML Stream Body$/', trim($document['content'][0]), 'Written document does not contain extracted result'); + $this->assertSame('HTML Stream Title', trim($document['title'][0]), 'Written document does not contain extracted title'); + $this->assertStringContainsString('HTML Stream Body', $document['content'][0], 'Written document does not contain extracted result'); $this->assertSame(['bar 3'], $document['attr_foo_3']); // now cleanup the documents to have the initial index state @@ -5128,6 +5126,23 @@ public function testInputEncoding(): void $this->assertCount(0, $result); } + /** + * Get the options to use for ManagedResources queries. + * + * @return array + */ + public function getManagedResourcesQueryOptions(): array + { + // percent-encode names and terms twice prior to Solr 10 as a workaround for SOLR-6853 + if (10 > self::$solrVersion) { + return [ + 'useDoubleEncoding' => true, + ]; + } + + return []; + } + /** * Get the options to use for ManagedResources Exists commands. * @@ -5135,8 +5150,8 @@ public function testInputEncoding(): void */ public function getManagedResourcesExistsCommandOptions(): array { - // Solr 7 can use HEAD requests because it's unaffected by SOLR-15116 and SOLR-16274 - if (7 === self::$solrVersion) { + // Solr 7 and Solr 10 can use HEAD requests because they're unaffected by SOLR-15116 and SOLR-16274 + if (7 === self::$solrVersion || 10 <= self::$solrVersion) { return [ 'useHeadRequest' => true, ]; @@ -5147,9 +5162,9 @@ public function getManagedResourcesExistsCommandOptions(): array public function testManagedStopwords(): void { - $query = self::$client->createManagedStopwords(); + $query = self::$client->createManagedStopwords($this->getManagedResourcesQueryOptions()); $query->setName('english'); - $term = 'managed_stopword_test'; + $term = '\'tis'; // Check that stopword list exists $exists = $query->createCommand($query::COMMAND_EXISTS, $this->getManagedResourcesExistsCommandOptions()); @@ -5177,13 +5192,13 @@ public function testManagedStopwords(): void // List stopwords $result = self::$client->execute($query); $this->assertTrue($result->getWasSuccessful()); - $this->assertContains('managed_stopword_test', $result->getItems()); + $this->assertContains($term, $result->getItems()); // List added stopword only $query->setTerm($term); $result = self::$client->execute($query); $this->assertTrue($result->getWasSuccessful()); - $this->assertSame(['managed_stopword_test'], $result->getItems()); + $this->assertSame([$term], $result->getItems()); // Delete added stopword $delete = $query->createCommand($query::COMMAND_DELETE); @@ -5209,16 +5224,16 @@ public function testManagedStopwords(): void /** * @testWith ["testlist", "managed_stopword_test"] - * ["list res-chars :/?#[]@%", "term res-chars :?#[]@%"] + * ["list res-chars :?#[]@", "term res-chars :?#[]@"] */ public function testManagedStopwordsCreation(string $name, string $term): void { // don't use invalid filename characters in list name on Windows to avoid running into SOLR-15895 if (self::$isSolrOnWindows) { - $name = str_replace([':', '/', '?'], '', $name); + $name = str_replace([':', '?'], '', $name); } - $query = self::$client->createManagedStopwords(); + $query = self::$client->createManagedStopwords($this->getManagedResourcesQueryOptions()); $query->setName($name.uniqid()); // Check that stopword list doesn't exist @@ -5295,9 +5310,10 @@ public function testManagedStopwordsCreation(string $name, string $term): void public function testManagedSynonyms(): void { - $query = self::$client->createManagedSynonyms(); + $query = self::$client->createManagedSynonyms($this->getManagedResourcesQueryOptions()); $query->setName('english'); - $term = 'managed_synonyms_test'; + $term = 'café'; + $synonymList = ['bar', 'pub']; // Check that synonym map exists $exists = $query->createCommand($query::COMMAND_EXISTS, $this->getManagedResourcesExistsCommandOptions()); @@ -5309,7 +5325,7 @@ public function testManagedSynonyms(): void $add = $query->createCommand($query::COMMAND_ADD); $synonyms = new Synonyms(); $synonyms->setTerm($term); - $synonyms->setSynonyms(['managed_synonym', 'synonym_test']); + $synonyms->setSynonyms($synonymList); $add->setSynonyms($synonyms); $query->setCommand($add); $result = self::$client->execute($query); @@ -5332,9 +5348,9 @@ public function testManagedSynonyms(): void $success = false; /** @var SynonymsResultItem $item */ foreach ($items as $item) { - if ('managed_synonyms_test' === $item->getTerm()) { + if ($term === $item->getTerm()) { $success = true; - $this->assertSame(['managed_synonym', 'synonym_test'], $item->getSynonyms()); + $this->assertSame($synonymList, $item->getSynonyms()); } } if (!$success) { @@ -5346,7 +5362,7 @@ public function testManagedSynonyms(): void $result = self::$client->execute($query); $this->assertTrue($result->getWasSuccessful()); $this->assertEquals( - [new SynonymsResultItem('managed_synonyms_test', ['managed_synonym', 'synonym_test'])], + [new SynonymsResultItem($term, $synonymList)], $result->getItems() ); @@ -5374,16 +5390,16 @@ public function testManagedSynonyms(): void /** * @testWith ["testmap", "managed_synonyms_test"] - * ["map res-chars :/?#[]@%", "term res-chars :?#[]@%"] + * ["map res-chars :?#[]@", "term res-chars :?#[]@"] */ public function testManagedSynonymsCreation(string $name, string $term): void { // don't use invalid filename characters in map name on Windows to avoid running into SOLR-15895 if (self::$isSolrOnWindows) { - $name = str_replace([':', '/', '?'], '', $name); + $name = str_replace([':', '?'], '', $name); } - $query = self::$client->createManagedSynonyms(); + $query = self::$client->createManagedSynonyms($this->getManagedResourcesQueryOptions()); $query->setName($name.uniqid()); // Check that synonym map doesn't exist @@ -5497,58 +5513,6 @@ public function testManagedResources(): void $this->assertSame(2, $n); } - /** - * Compare our fix for Solr requiring special characters be doubly percent-encoded - * with an RFC 3986 compliant implementation that uses single percent-encoding. - * - * This test checks the behaviour against the latest releases of Solr 8 or Solr 9. - * Prior to Solr 8.11.4 Solr 9.7.0 compliant requests failed in a different way. - * This test will fail against versions prior to those releases for the wrong - * (in the context of what we're testing) reasons. - * - * If this test fails, Solr has probably fixed SOLR-6853 on their side. If that is - * the case, we'll have to re-evaluate what to do about the workaround. As long as - * no other tests fail, they're still supporting the workaround for BC. - * - * While SOLR-6853 has been closed since we implemented the workaround, the bug - * wasn't actually fixed and the workaround currently remains necessary. - * - * @see https://issues.apache.org/jira/browse/SOLR-6853 - * @see https://github.com/solariumphp/solarium/pull/742 - * - * @testWith ["stopwords"] - * ["synonyms"] - */ - public function testManagedResourcesSolr6853(string $resourceType): void - { - if (7 >= self::$solrVersion) { - $this->expectNotToPerformAssertions(); - - return; - } - - $compliantRequestBuilder = new CompliantManagedResourceRequestBuilder(); - - $query = match ($resourceType) { - 'stopwords' => new StopwordsQuery(), - 'synonyms' => new SynonymsQuery(), - }; - $query->setName('english'); - $query->setTerm('test-:/?#[]@% '); - - // Getting the resource with a compliant request builder doesn't work - $request = $compliantRequestBuilder->build($query); - $this->assertStringEndsWith('/test-%3A%2F%3F%23%5B%5D%40%25%20', $request->getHandler()); - - $response = self::$client->executeRequest($request); - $responseBody = json_decode($response->getBody(), true); - $this->assertSame(500, $responseBody['error']['code'], 'Check if SOLR-6853 is fixed.'); - $this->assertSame('URLDecoder: Incomplete trailing escape (%) pattern', $responseBody['error']['msg'], 'Check if SOLR-6853 is fixed.'); - - $this->expectException(HttpException::class); - self::$client->createResult($query, $response); - } - public function testGetBodyOnHttpError(): void { /** @var Status4xxNoExceptionInterface $query */ @@ -5660,28 +5624,3 @@ public function getHelper(): Helper return $this->helper; } } - -/** - * Request builder for a managed resource that percent-encodes the list/map name - * and term once, in compliance wiht RFC 3986. - * - * It doesn't apply the double percent-encoding required to work around SOLR-6853 - * - * @see https://issues.apache.org/jira/browse/SOLR-6853 - */ -class CompliantManagedResourceRequestBuilder extends ResourceRequestBuilder -{ - public function build(QueryInterface|AbstractManagedResourcesQuery $query): Request - { - $request = parent::build($query); - - // Undo the double percent-encoding to end up with single encoding - $handlerSegments = explode('/', $request->getHandler()); - foreach ($handlerSegments as &$segment) { - $segment = rawurldecode($segment); - } - $request->setHandler(implode('/', $handlerSegments)); - - return $request; - } -} diff --git a/tests/Integration/Fixtures/conf/schema10.patch b/tests/Integration/Fixtures/conf/schema10.patch new file mode 100644 index 000000000..eeb4dba1e --- /dev/null +++ b/tests/Integration/Fixtures/conf/schema10.patch @@ -0,0 +1,22 @@ +diff --git a/solr/server/solr/configsets/sample_techproducts_configs/conf/managed-schema.xml b/solr/server/solr/configsets/sample_techproducts_configs/conf/managed-schema.xml +index 190214aebad..7a76a28276a 100644 +--- a/solr/server/solr/configsets/sample_techproducts_configs/conf/managed-schema.xml ++++ b/solr/server/solr/configsets/sample_techproducts_configs/conf/managed-schema.xml +@@ -119,7 +119,9 @@ + +- ++ ++ ++ + + +- http://localhost:9998 ++ http://tika:9998 + + + true +@@ -698,6 +698,39 @@ + 5 + + ++ ++ ++ ++ ++ ++ ++ explicit ++ ++ ++ text ++ edismax ++ ++ text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4 ++ title^10.0 description^5.0 keywords^5.0 author^2.0 resourcename^1.0 ++ ++ 100% ++ *:* ++ 10 ++ *,score ++ ++ on ++ on ++ on ++ ++ ++ ++ ++ spellcheck ++ ++ ++ +