This is an automated email from the ASF dual-hosted git repository. dsmiley pushed a commit to branch branch_10x in repository https://gitbox.apache.org/repos/asf/solr.git
commit 11479090b92af9a0e1bdd025a43383f3207fa4ce Author: Jalaz Kumar <[email protected]> AuthorDate: Wed Oct 21 02:01:38 2026 +0530 SOLR-16396: Align v2 full-config section keys with pluralised forms (#5175) Co-authored-by: Eric Pugh <[email protected]> Co-authored-by: Claude Sonnet 5 <[email protected]> (cherry picked from commit d5a17012b2cf9e3ba903693e57b14fb6933aa1c7) --- .../org/apache/solr/handler/SolrConfigHandler.java | 4 +- .../apache/solr/handler/admin/api/GetConfig.java | 46 +++++++++++++++++++++- .../apache/solr/core/TestSolrConfigHandler.java | 33 +++++++++++----- .../handler/admin/api/GetConfigSolrCloudTest.java | 3 +- .../solr/handler/admin/api/GetConfigTest.java | 8 +++- .../configuration-guide/pages/config-api.adoc | 8 +++- 6 files changed, 86 insertions(+), 16 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java index 1f9fe5f1e94..d01bf0d07eb 100644 --- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java @@ -189,7 +189,9 @@ public class SolrConfigHandler extends RequestHandlerBase if (parts.size() == 1) { // this is the whole config. sent out the whole payload V2ApiUtils.squashIntoSolrResponseWithoutHeader( - resp, new GetConfig(req).getConfig(req.getParams().getBool("expandParams", false))); + resp, + new GetConfig(req) + .getConfigWithInternalNames(req.getParams().getBool("expandParams", false))); } else { if (ConfigOverlay.NAME.equals(parts.get(1))) { V2ApiUtils.squashIntoSolrResponseWithoutHeader(resp, new GetConfig(req).getOverlay()); diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/GetConfig.java b/solr/core/src/java/org/apache/solr/handler/admin/api/GetConfig.java index b8efea0a11c..5978cb8e0f2 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/GetConfig.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/GetConfig.java @@ -24,6 +24,7 @@ import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_READ_P import jakarta.inject.Inject; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -70,13 +71,25 @@ public class GetConfig extends JerseyResource implements ConfigApi.Get { public static final String REQUEST_HANDLERS = "requestHandlers"; - // v2-only renames: external (URL/response-key) component name -> internal SolrConfig section - // name. v1's GET /config/{component} keeps using the internal names unchanged. + // v2-only renames for /config/{component}(/{componentName}): external name -> internal + // SolrConfig section name. Excludes requestHandler, which has its own dedicated endpoint instead + // of routing through these paths. private static final Map<String, String> EXTERNAL_TO_INTERNAL_COMPONENT_NAME = Map.of( "searchComponents", "searchComponent", "queryResponseWriters", "queryResponseWriter"); + // Same, but for the full /config dump's top-level keys, which also renames requestHandler. + // Derived from the map above so the two can't drift apart. + private static final Map<String, String> EXTERNAL_TO_INTERNAL_FULL_CONFIG_NAME = + buildFullConfigNameMap(); + + private static Map<String, String> buildFullConfigNameMap() { + Map<String, String> map = new HashMap<>(EXTERNAL_TO_INTERNAL_COMPONENT_NAME); + map.put(REQUEST_HANDLERS, SolrRequestHandler.TYPE); + return Map.copyOf(map); + } + private static String toInternalComponentName(String externalName) { return EXTERNAL_TO_INTERNAL_COMPONENT_NAME.getOrDefault(externalName, externalName); } @@ -90,6 +103,15 @@ public class GetConfig extends JerseyResource implements ConfigApi.Get { return internalName; } + private static String toExternalFullConfigName(String internalName) { + for (Map.Entry<String, String> e : EXTERNAL_TO_INTERNAL_FULL_CONFIG_NAME.entrySet()) { + if (e.getValue().equals(internalName)) { + return e.getKey(); + } + } + return internalName; + } + private final SolrQueryRequest solrQueryRequest; @Inject @@ -100,11 +122,31 @@ public class GetConfig extends JerseyResource implements ConfigApi.Get { @Override @PermissionName(CONFIG_READ_PERM) public ConfigInfoResponse getConfig(boolean expandParams) { + final var response = instantiateJerseyResponse(ConfigInfoResponse.class); + response.config = renameToExternalFullConfigNames(buildConfigMap(expandParams)); + return response; + } + + /** + * Same as {@link #getConfig(boolean)}, but keeps internal section names (e.g. {@code + * searchComponent}, {@code requestHandler}) instead of the v2-only pluralized forms. Used by + * {@link SolrConfigHandler}'s legacy v1 {@code GET /config}, which must preserve the original + * names for backward compatibility. + */ + public ConfigInfoResponse getConfigWithInternalNames(boolean expandParams) { final var response = instantiateJerseyResponse(ConfigInfoResponse.class); response.config = buildConfigMap(expandParams); return response; } + private Map<String, Object> renameToExternalFullConfigNames(Map<String, Object> configMap) { + Map<String, Object> reMappedConfigMap = new NullKeyTolerantMap("children"); + for (Map.Entry<String, Object> e : configMap.entrySet()) { + reMappedConfigMap.put(toExternalFullConfigName(e.getKey()), e.getValue()); + } + return reMappedConfigMap; + } + @Override @PermissionName(CONFIG_READ_PERM) public ConfigOverlayResponse getOverlay() { diff --git a/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java b/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java index 4cb5b8889b6..1f16c3ab7ff 100644 --- a/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java +++ b/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java @@ -98,13 +98,24 @@ public class TestSolrConfigHandler extends RestTestBase { solrTestRule.reset(); } + /** + * The full "/config" response pluralizes some section names (e.g. searchComponent -> + * searchComponents, requestHandler -> requestHandlers) under the V2 API (see + * GetConfig#getConfig), but not under V1. + */ + private static String fullConfigKey( + RestTestHarness harness, String internalName, String externalName) { + return harness.getBaseURL().contains("/____v2/") ? externalName : internalName; + } + public void testProperty() throws Exception { RestTestHarness harness = restTestHarness; + String requestHandlerKey = fullConfigKey(harness, "requestHandler", "requestHandlers"); MapWriter confMap = getRespMap(harness, "/config"); - assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/luke"), null)); - assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/info"), null)); - assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/file"), null)); - assertNotNull(confMap._get(asList("config", "requestHandler", "/admin/ping"), null)); + assertNotNull(confMap._get(asList("config", requestHandlerKey, "/admin/luke"), null)); + assertNotNull(confMap._get(asList("config", requestHandlerKey, "/admin/info"), null)); + assertNotNull(confMap._get(asList("config", requestHandlerKey, "/admin/file"), null)); + assertNotNull(confMap._get(asList("config", requestHandlerKey, "/admin/ping"), null)); String payload = "{\n" @@ -188,6 +199,10 @@ public class TestSolrConfigHandler extends RestTestBase { } public static void reqhandlertests(RestTestHarness writeHarness) throws Exception { + // The per-component endpoints used elsewhere in this test are unaffected by the full-config + // pluralization below, since they already use the pluralized name. + String searchComponentKey = fullConfigKey(writeHarness, "searchComponent", "searchComponents"); + String requestHandlerKey = fullConfigKey(writeHarness, "requestHandler", "requestHandlers"); String payload = "{\n" + "'create-requesthandler' : { 'name' : '/x', 'class': 'org.apache.solr.handler.DumpRequestHandler' , 'startup' : 'lazy'}\n" @@ -332,7 +347,7 @@ public class TestSolrConfigHandler extends RestTestBase { testForResponseElement( writeHarness, "/config", - asList("config", "searchComponent", "tc", "class"), + asList("config", searchComponentKey, "tc", "class"), "org.apache.solr.handler.component.TermsComponent", TIMEOUT_S); payload = @@ -343,14 +358,14 @@ public class TestSolrConfigHandler extends RestTestBase { testForResponseElement( writeHarness, "/config", - asList("config", "searchComponent", "tc", "class"), + asList("config", searchComponentKey, "tc", "class"), "org.apache.solr.handler.component.TermVectorComponent", TIMEOUT_S); payload = "{\n" + "'delete-searchcomponent' : 'tc'" + "}"; runConfigCommand(writeHarness, "/config", payload); testForResponseElement( - writeHarness, "/config", asList("config", "searchComponent", "tc"), null, TIMEOUT_S); + writeHarness, "/config", asList("config", searchComponentKey, "tc"), null, TIMEOUT_S); // <valueSourceParser name="countUsage" // class="org.apache.solr.core.CountUsageValueSourceParser"/> payload = @@ -444,7 +459,7 @@ public class TestSolrConfigHandler extends RestTestBase { testForResponseElement( writeHarness, "/config", - asList("config", "searchComponent", "myspellcheck", "spellchecker", "class"), + asList("config", searchComponentKey, "myspellcheck", "spellchecker", "class"), "solr.DirectSolrSpellChecker", TIMEOUT_S); @@ -463,7 +478,7 @@ public class TestSolrConfigHandler extends RestTestBase { testForResponseElement( writeHarness, "/config", - asList("config", "requestHandler", "/dump100", "class"), + asList("config", requestHandlerKey, "/dump100", "class"), "org.apache.solr.handler.DumpRequestHandler", TIMEOUT_S); diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java index 20dbd09d1a6..8ae8b63afbf 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java @@ -58,7 +58,8 @@ public class GetConfigSolrCloudTest extends SolrCloudTestCase { assertTrue(response.config.containsKey("luceneMatchVersion")); assertTrue(response.config.containsKey("updateHandler")); assertTrue(response.config.containsKey("query")); - assertTrue(response.config.containsKey("requestHandler")); + assertFalse(response.config.containsKey("requestHandler")); + assertTrue(response.config.containsKey("requestHandlers")); } @Test diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigTest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigTest.java index 4407845d155..c9c836405f8 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigTest.java @@ -55,8 +55,12 @@ public class GetConfigTest extends SolrTestCase { assertTrue(response.config.containsKey("luceneMatchVersion")); assertTrue(response.config.containsKey("updateHandler")); assertTrue(response.config.containsKey("query")); - assertTrue(response.config.containsKey("requestHandler")); - assertTrue(response.config.containsKey("searchComponent")); + assertFalse(response.config.containsKey("requestHandler")); + assertTrue(response.config.containsKey("requestHandlers")); + + assertFalse(response.config.containsKey("searchComponent")); + assertTrue(response.config.containsKey("searchComponents")); + assertTrue(response.config.containsKey("updateProcessor")); } diff --git a/solr/solr-ref-guide/modules/configuration-guide/pages/config-api.adoc b/solr/solr-ref-guide/modules/configuration-guide/pages/config-api.adoc index 01c043ae6a6..cbf7836a4f2 100644 --- a/solr/solr-ref-guide/modules/configuration-guide/pages/config-api.adoc +++ b/solr/solr-ref-guide/modules/configuration-guide/pages/config-api.adoc @@ -74,7 +74,9 @@ V2 API:: http://localhost:8983/api/collections/techproducts/config ---- -The response is the same merged configuration as the V1 API, with one difference: a plugin's unnamed child elements are grouped under a self-describing `children` key instead of an empty string, since Solr's V2 JSON serialization library does not allow empty-string keys: +The response is the same merged configuration as the V1 API, with three differences. + +First, a plugin's unnamed child elements are grouped under a self-describing `children` key instead of an empty string, since Solr's V2 JSON serialization library does not allow empty-string keys: [source,json] ---- @@ -87,6 +89,10 @@ The response is the same merged configuration as the V1 API, with one difference ] }] ---- + +Second, the `searchComponent` and `queryResponseWriter` sections are renamed to their pluralized forms, `searchComponents` and `queryResponseWriters`, as described below. + +Third, the `requestHandler` section is similarly renamed to `requestHandlers`, matching the dedicated `/config/requestHandlers` endpoint described below. ==== ======
