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 e3cf09d8558a5d86e0360b5a851704711db59942
Author: Jalaz Kumar <[email protected]>
AuthorDate: Wed Oct 21 02:46:16 2026 +0530

    SOLR-16396: Migrate remaining GET /config/{component} sections to JAX-RS 
(#5174)
    
    (cherry picked from commit 8576dc8974a8e4ceef57a3ce6ec6685c86c8bac8)
---
 .../SOLR-16396-config-remaining-get-sections.yml   | 13 ++++
 .../apache/solr/client/api/endpoint/ConfigApi.java | 17 ++---
 .../org/apache/solr/handler/SolrConfigHandler.java | 13 +---
 .../apache/solr/handler/admin/api/GetConfig.java   | 45 +++++++++++--
 .../solr/handler/admin/api/GetConfigAPI.java       | 38 +----------
 .../solr/handler/admin/V2ConfigAPIMappingTest.java | 12 ----
 .../solr/handler/admin/api/GetConfigTest.java      | 76 +++++++++++++++++++---
 .../configuration-guide/pages/config-api.adoc      | 19 ++++--
 8 files changed, 148 insertions(+), 85 deletions(-)

diff --git a/changelog/unreleased/SOLR-16396-config-remaining-get-sections.yml 
b/changelog/unreleased/SOLR-16396-config-remaining-get-sections.yml
new file mode 100644
index 00000000000..b2b23a0cf94
--- /dev/null
+++ b/changelog/unreleased/SOLR-16396-config-remaining-get-sections.yml
@@ -0,0 +1,13 @@
+title: >
+  Migrate remaining GET /config/{component} sections to JAX-RS.
+  V2 renames listener, queryParser, valueSourceParser, cache, transformer, 
updateProcessor, queryConverter and expressible to
+  listeners, queryParsers, valueSourceParsers, caches, transformers, 
updateProcessors, queryConverters and expressibles respectively.  (V1 unchanged)
+type: changed
+authors:
+  - name: Jalaz Kumar
+    nick: jaykay12
+  - name: Eric Pugh
+    nick: epugh
+links:
+  - name: SOLR-16396
+    url: https://issues.apache.org/jira/browse/SOLR-16396
diff --git 
a/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigApi.java 
b/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigApi.java
index f10026be924..70aaf6c6b19 100644
--- a/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigApi.java
+++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigApi.java
@@ -91,12 +91,10 @@ public interface ConfigApi {
             @Parameter(description = "Include package metadata 
(_packageinfo_)")
             boolean meta);
 
-    // TODO: Keep this regex in sync with GetConfig.MIGRATED_CONFIG_COMPONENTS 
("searchComponents"
-    // and "queryResponseWriters" here are the external, pluralized names for 
the internal
-    // "searchComponent"/"queryResponseWriter" sections; see 
GetConfig#toExternalComponentName)
+    // TODO: Keep this regex in sync with GetConfig.MIGRATED_CONFIG_COMPONENTS
     @GET
     @Path(
-        
"/{component:updateHandler|directoryFactory|indexConfig|codecFactory|query|searchComponents|queryResponseWriters}")
+        
"/{component:updateHandler|directoryFactory|indexConfig|codecFactory|query|searchComponents|queryResponseWriters|luceneMatchVersion|initParams|listeners|requestDispatcher|jmx|queryParsers|valueSourceParsers|transformers|updateProcessors|updateRequestProcessorChain|queryConverters|caches|circuitBreaker|expressibles|schemaFactory|restManager|statsCache|recoveryStrategy|indexReaderFactory|indexConfigdeletionPolicy|updateHandlerupdateLog}")
     @StoreApiParameters
     @Operation(
         summary = "Fetch a config section",
@@ -105,12 +103,15 @@ public interface ConfigApi {
         @PathParam("component") @Parameter(description = "Name of the config 
component")
             String component);
 
-    // TODO: Keep this regex in sync with GetConfig.MIGRATED_CONFIG_COMPONENTS 
("searchComponents"
-    // and "queryResponseWriters" here are the external, pluralized names for 
the internal
-    // "searchComponent"/"queryResponseWriter" sections; see 
GetConfig#toExternalComponentName)
+    // TODO: Keep this regex in sync with 
GetConfig.MIGRATED_CONFIG_COMPONENTS. Sections without
+    // REQUIRE_NAME in SolrConfig's plugin registration (initParams, listeners,
+    // updateRequestProcessorChain, circuitBreaker) are deliberately excluded 
here: they're
+    // serialized as a List rather than a name-keyed Map, so by-name lookup 
isn't meaningful for
+    // them (see GetConfig.filterByComponentName).
+    // Scalars / non-plugin sections (luceneMatchVersion, jmx etc) are also 
omitted.
     @GET
     @Path(
-        
"/{component:updateHandler|directoryFactory|indexConfig|codecFactory|query|searchComponents|queryResponseWriters}/{componentName}")
+        
"/{component:updateHandler|directoryFactory|indexConfig|codecFactory|query|searchComponents|queryResponseWriters|queryParsers|valueSourceParsers|transformers|updateProcessors|queryConverters|caches|expressibles|schemaFactory|restManager|statsCache|recoveryStrategy|indexReaderFactory}/{componentName}")
     @StoreApiParameters
     @Operation(
         summary = "Fetch a single named entry within a config section",
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 d01bf0d07eb..9e43499fbe3 100644
--- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
@@ -236,16 +236,9 @@ public class SolrConfigHandler extends RequestHandlerBase
                     : getConfig.getConfigComponentByName(
                         parts.get(1), componentName, 
req.getParams().getBool("meta", false)));
           } else {
-            Map<String, Object> m =
-                Utils.convertToMap(req.getCore().getSolrConfig(), new 
LinkedHashMap<>());
-            Map<String, Object> val = new LinkedHashMap<>();
-            val.put(parts.get(1), m.get(parts.get(1)));
-            String componentName = req.getParams().get("componentName");
-            if (componentName != null) {
-              GetConfig.filterByComponentName(
-                  val, parts.get(1), componentName, 
req.getParams().getBool("meta", false), req);
-            }
-            resp.add("config", val);
+            throw new SolrException(
+                SolrException.ErrorCode.NOT_FOUND,
+                "No config API for path: /config/" + parts.get(1));
           }
         }
       }
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 5978cb8e0f2..cd89b1d3e9e 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
@@ -67,7 +67,28 @@ public class GetConfig extends JerseyResource implements 
ConfigApi.Get {
           "codecFactory",
           "query",
           "searchComponent",
-          "queryResponseWriter");
+          "queryResponseWriter",
+          "luceneMatchVersion",
+          "initParams",
+          "listener",
+          "requestDispatcher",
+          "jmx",
+          "queryParser",
+          "valueSourceParser",
+          "transformer",
+          "updateProcessor",
+          "updateRequestProcessorChain",
+          "queryConverter",
+          "cache",
+          "circuitBreaker",
+          "expressible",
+          "schemaFactory",
+          "restManager",
+          "statsCache",
+          "recoveryStrategy",
+          "indexReaderFactory",
+          "indexConfigdeletionPolicy",
+          "updateHandlerupdateLog");
 
   public static final String REQUEST_HANDLERS = "requestHandlers";
 
@@ -77,7 +98,15 @@ public class GetConfig extends JerseyResource implements 
ConfigApi.Get {
   private static final Map<String, String> EXTERNAL_TO_INTERNAL_COMPONENT_NAME 
=
       Map.of(
           "searchComponents", "searchComponent",
-          "queryResponseWriters", "queryResponseWriter");
+          "queryResponseWriters", "queryResponseWriter",
+          "listeners", "listener",
+          "queryParsers", "queryParser",
+          "valueSourceParsers", "valueSourceParser",
+          "transformers", "transformer",
+          "updateProcessors", "updateProcessor",
+          "queryConverters", "queryConverter",
+          "expressibles", "expressible",
+          "caches", "cache");
 
   // 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.
@@ -239,10 +268,16 @@ public class GetConfig extends JerseyResource implements 
ConfigApi.Get {
       String componentName,
       boolean meta,
       SolrQueryRequest req) {
-    Map pluginNameVsPluginInfo = (Map) configMap.get(component);
-    if (pluginNameVsPluginInfo == null) {
+    Object sectionValue = configMap.get(component);
+    if (sectionValue == null) {
       return;
     }
+    if (!(sectionValue instanceof Map) && !(sectionValue instanceof 
MapWriter)) {
+      throw new SolrException(
+          SolrException.ErrorCode.NOT_FOUND,
+          component + " does not support filtering by componentName");
+    }
+    Map pluginNameVsPluginInfo = (Map) sectionValue;
 
     Object o =
         pluginNameVsPluginInfo instanceof MapWriter
@@ -335,7 +370,7 @@ public class GetConfig extends JerseyResource implements 
ConfigApi.Get {
     return reqHandlersMap;
   }
 
-  @SuppressWarnings({"unchecked", "rawtypes"})
+  @SuppressWarnings("unchecked")
   private Map<String, Object> expandUseParams(SolrQueryRequest req, Object 
plugin) {
     Map<String, Object> pluginInfo;
     if (plugin instanceof Map) {
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/GetConfigAPI.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/GetConfigAPI.java
index 6cd43791100..110974eec68 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/GetConfigAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/GetConfigAPI.java
@@ -28,9 +28,8 @@ import org.apache.solr.response.SolrQueryResponse;
 /**
  * V2 APIs for retrieving some or all configuration relevant to a particular 
collection (or core).
  *
- * <p>This class covers legacy AnnotatedApi paths under GET /config: /params, 
/params{paramset}, and
- * remaining {@code /config/{component}} sections not yet migrated to {@link
- * org.apache.solr.client.api.endpoint.ConfigApi.Get}
+ * <p>This class only covers legacy AnnotatedApi paths under GET /config: 
/params and
+ * /params{paramset}
  */
 public class GetConfigAPI {
   private final SolrConfigHandler configHandler;
@@ -54,37 +53,4 @@ public class GetConfigAPI {
   public void getSingleParamSet(SolrQueryRequest req, SolrQueryResponse rsp) {
     configHandler.handleRequest(req, rsp);
   }
-
-  // GET /config/... sections still via SolrConfigHandler.
-  // Every name SolrConfig.writeMap can emit.
-  // Delete a path from this list when that section is migrated to Jersey
-  @EndPoint(
-      path = {
-        "/config/luceneMatchVersion",
-        "/config/initParams",
-        "/config/listener",
-        "/config/requestDispatcher",
-        "/config/jmx",
-        "/config/queryParser",
-        "/config/valueSourceParser",
-        "/config/transformer",
-        "/config/updateProcessor",
-        "/config/updateRequestProcessorChain",
-        "/config/queryConverter",
-        "/config/cache",
-        "/config/circuitBreaker",
-        "/config/expressible",
-        "/config/schemaFactory",
-        "/config/restManager",
-        "/config/statsCache",
-        "/config/recoveryStrategy",
-        "/config/indexReaderFactory",
-        "/config/indexConfigdeletionPolicy",
-        "/config/updateHandlerupdateLog"
-      },
-      method = GET,
-      permission = CONFIG_READ_PERM)
-  public void getComponentConfig(SolrQueryRequest req, SolrQueryResponse rsp) {
-    configHandler.handleRequest(req, rsp);
-  }
 }
diff --git 
a/solr/core/src/test/org/apache/solr/handler/admin/V2ConfigAPIMappingTest.java 
b/solr/core/src/test/org/apache/solr/handler/admin/V2ConfigAPIMappingTest.java
index e7912afa8fa..a671cfce9f2 100644
--- 
a/solr/core/src/test/org/apache/solr/handler/admin/V2ConfigAPIMappingTest.java
+++ 
b/solr/core/src/test/org/apache/solr/handler/admin/V2ConfigAPIMappingTest.java
@@ -44,18 +44,6 @@ public class V2ConfigAPIMappingTest extends 
V2ApiMappingTest<SolrConfigHandler>
     apiBag.registerObject(new ModifyParamSetAPI(getRequestHandler()));
   }
 
-  // GET /v2/collectionName/config/<component> is a pure pass-through to the 
underlying request
-  // handler.  Just check
-  // the API lookup works for a handful of the valid config "components".
-  @Test
-  public void testGetSingleComponentConfig() {
-    assertAnnotatedApiExistsFor("GET", "/config/jmx");
-    assertAnnotatedApiExistsFor("GET", "/config/requestDispatcher");
-    assertAnnotatedApiExistsFor("GET", "/config/luceneMatchVersion");
-    assertAnnotatedApiExistsFor("GET", "/config/indexConfigdeletionPolicy");
-    assertAnnotatedApiExistsFor("GET", "/config/updateHandlerupdateLog");
-  }
-
   @Test
   public void testGetParamsetsConfig() {
     assertAnnotatedApiExistsFor("GET", "/config/params");
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 c9c836405f8..059f452f0b2 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
@@ -19,6 +19,7 @@ package org.apache.solr.handler.admin.api;
 import static org.apache.solr.core.CoreContainer.ALLOW_PATHS_SYSPROP;
 import static org.apache.solr.handler.admin.api.GetConfig.REQUEST_HANDLERS;
 
+import java.util.List;
 import java.util.Map;
 import org.apache.solr.SolrTestCase;
 import org.apache.solr.client.api.model.IndexType;
@@ -242,6 +243,37 @@ public class GetConfigTest extends SolrTestCase {
     assertTrue(querySection.containsKey("filterCache"));
   }
 
+  @Test
+  @SuppressWarnings("unchecked")
+  public void testGetConfigComponentSectionShapes() throws Exception {
+    var queryResponse =
+        new ConfigApi.GetConfigComponent(IndexType.CORE, CORE_NAME, 
"luceneMatchVersion")
+            .process(solrTestRule.getSolrClient(CORE_NAME));
+    assertNull(queryResponse.error);
+    assertTrue(queryResponse.config.get("luceneMatchVersion") instanceof 
String);
+
+    queryResponse =
+        new ConfigApi.GetConfigComponent(IndexType.CORE, CORE_NAME, 
"initParams")
+            .process(solrTestRule.getSolrClient(CORE_NAME));
+    assertNull(queryResponse.error);
+    assertTrue(queryResponse.config.get("initParams") instanceof List);
+
+    queryResponse =
+        new ConfigApi.GetConfigComponent(IndexType.CORE, CORE_NAME, 
"requestDispatcher")
+            .process(solrTestRule.getSolrClient(CORE_NAME));
+    assertNull(queryResponse.error);
+    assertTrue(queryResponse.config.get("requestDispatcher") instanceof Map);
+
+    queryResponse =
+        new ConfigApi.GetConfigComponent(IndexType.CORE, CORE_NAME, 
"updateProcessors")
+            .process(solrTestRule.getSolrClient(CORE_NAME));
+    assertNull(queryResponse.error);
+    Map<String, Object> querySection =
+        (Map<String, Object>) queryResponse.config.get("updateProcessors");
+    assertNotNull(querySection);
+    assertTrue(querySection.containsKey("uuid"));
+  }
+
   @Test
   public void testGetConfigComponentByUnknownNameReturnsNotFound() {
     var request =
@@ -255,14 +287,42 @@ public class GetConfigTest extends SolrTestCase {
   }
 
   @Test
-  public void testGetConfigComponentRejectsOldSingularSearchComponentPath() {
-    // v2 intentionally renamed this one section to the plural 
"searchComponents" in the URL;
+  public void testGetConfigComponentByNameOnListSectionReturnsNotFound() {
+    // List-shaped sections are excluded from the by-name @Path; Jersey should 
return 404.
+    for (String component :
+        List.of("listeners", "initParams", "updateRequestProcessorChain", 
"circuitBreaker")) {
+      var request =
+          new ConfigApi.GetConfigComponentByName(IndexType.CORE, CORE_NAME, 
component, "random");
+      final RemoteSolrException ex =
+          expectThrows(
+              RemoteSolrException.class,
+              () -> request.process(solrTestRule.getSolrClient(CORE_NAME)));
+      assertEquals(404, ex.code());
+    }
+  }
+
+  @Test
+  public void testGetConfigComponentRejectsOldSingularPaths() {
+    // v2-only plurals;
     // the old singular form (still used by v1) shouldn't match the v2 route 
at all.
-    var request = new ConfigApi.GetConfigComponent(IndexType.CORE, CORE_NAME, 
"searchComponent");
-    final RemoteSolrException ex =
-        expectThrows(
-            RemoteSolrException.class,
-            () -> request.process(solrTestRule.getSolrClient(CORE_NAME)));
-    assertEquals(404, ex.code());
+    for (String component :
+        List.of(
+            "searchComponent",
+            "queryResponseWriter",
+            "listener",
+            "queryParser",
+            "valueSourceParser",
+            "transformer",
+            "updateProcessor",
+            "queryConverter",
+            "expressible",
+            "cache")) {
+      var request = new ConfigApi.GetConfigComponent(IndexType.CORE, 
CORE_NAME, component);
+      final RemoteSolrException ex =
+          expectThrows(
+              RemoteSolrException.class,
+              () -> request.process(solrTestRule.getSolrClient(CORE_NAME)));
+      assertEquals(404, ex.code());
+    }
   }
 }
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 cbf7836a4f2..42e1f77c794 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
@@ -124,8 +124,12 @@ The output will be details of each request handler defined 
in `solrconfig.xml`,
 To see the configuration for implicit request handlers, add 
`expandParams=true` to the request.
 See the documentation for implicit request handlers linked above for examples 
using this command.
 
-The available top-level sections that can be added as path parameters are: 
`query`, `requestHandler`, `searchComponent`, `updateHandler`, 
`queryResponseWriter`, `initParams`, `znodeVersion`, `listener`, 
`directoryFactory`, `indexConfig`, and `codecFactory`.
-In the V2 API, `requestHandler`, `searchComponent` and `queryResponseWriter` 
are exposed under the pluralized names `requestHandlers`, `searchComponents` 
and `queryResponseWriters` instead (including the response JSON key).
+The available top-level sections that can be added as path parameters include: 
`query`, `requestHandler`, `searchComponent`, `updateHandler`, 
`queryResponseWriter`, `initParams`, `znodeVersion`, `listener`, 
`directoryFactory`, `indexConfig`, and `codecFactory`.
+(Other `solrconfig.xml` sections emitted by Solr may also be fetched the same 
way)
+
+In the V2 API, several multi-valued sections use pluralized path names (and 
the same plural as the response JSON key).
+
+V1 paths keep the singular / internal names unchanged.
 
 To further restrict the request to a single component within a top-level 
section, use the `componentName` request parameter.
 
@@ -166,9 +170,10 @@ The output of this command will look similar to(V1 nests 
under `requestHandler`;
 }
 ----
 
-The ability to restrict to objects within a top-level section is limited to 
request handlers (`requestHandler`), search components (`searchComponent`), and 
response writers (`queryResponseWriter`).
+By-name lookup applies to sections that are maps of named plugins, such as 
request handlers, search components, response writers etc.
+List-shaped sections like initParams, listeners, updateRequestProcessorChain 
etc cannot be filtered by name.
 
-For search components and response writers, the V2 API uses a path segment 
instead of the `componentName` request parameter, under the pluralized section 
names.
+For those name-keyed sections other than the request handler, the V2 API uses 
a path segment instead of the `componentName` request parameter, under the 
pluralized section names.
 For example, to return configuration for the `elevator` search component:
 
 [tabs#getnamedcomponent]
@@ -191,7 +196,7 @@ 
http://localhost:8983/api/collections/techproducts/config/searchComponents/eleva
 ====
 ======
 
-The same applies to response writers.
+The same pattern applies to other named-key sections.
 For example, to return configuration for the `xslt` response writer:
 
 [tabs#getnamedwriter]
@@ -202,6 +207,7 @@ V1 API::
 [source,bash]
 ----
 
http://localhost:8983/solr/techproducts/config/queryResponseWriter?componentName=xslt
+http://localhost:8983/solr/techproducts/config/updateProcessor?componentName=uuid
 ----
 ====
 V2 API::
@@ -210,11 +216,12 @@ V2 API::
 [source,bash]
 ----
 
http://localhost:8983/api/collections/techproducts/config/queryResponseWriters/xslt
+http://localhost:8983/api/collections/techproducts/config/updateProcessors/uuid
 ----
 ====
 ======
 
-Request handlers are unaffected and continue to use `componentName` in both 
APIs, as shown above.
+Request handlers are a special case and continue to use `componentName` in 
both APIs, as shown above.
 
 == Commands to Modify the Config
 

Reply via email to