This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new 714d6ceff85 SOLR-16396 | Migrate GET config sections query,
searchComponent & queryResponseWriter to JAX-RS (#5125)
714d6ceff85 is described below
commit 714d6ceff852e6dc743ef51ae094e4eaf8f3b3f9
Author: Jalaz Kumar <[email protected]>
AuthorDate: Thu Oct 15 23:03:39 2026 +0530
SOLR-16396 | Migrate GET config sections query, searchComponent &
queryResponseWriter to JAX-RS (#5125)
Co-authored-by: Eric Pugh <[email protected]>
---
changelog/unreleased/SOLR-16396-config-query.yml | 11 ++
.../apache/solr/client/api/endpoint/ConfigApi.java | 27 ++++-
.../org/apache/solr/handler/SolrConfigHandler.java | 60 ++---------
.../apache/solr/handler/admin/api/GetConfig.java | 119 ++++++++++++++++++++-
.../solr/handler/admin/api/GetConfigAPI.java | 3 -
.../solr/handler/admin/V2ConfigAPIMappingTest.java | 2 +-
.../handler/admin/api/GetConfigSolrCloudTest.java | 5 +-
.../solr/handler/admin/api/GetConfigTest.java | 82 +++++++++++++-
.../configuration-guide/pages/config-api.adoc | 49 +++++++++
9 files changed, 293 insertions(+), 65 deletions(-)
diff --git a/changelog/unreleased/SOLR-16396-config-query.yml
b/changelog/unreleased/SOLR-16396-config-query.yml
new file mode 100644
index 00000000000..b51ffafa9a2
--- /dev/null
+++ b/changelog/unreleased/SOLR-16396-config-query.yml
@@ -0,0 +1,11 @@
+title:
+ Migrate GET config sections query, searchComponent & queryResponseWriter to
JAX-RS
+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 baaf4a9f785..49a4b018cc9 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
@@ -70,9 +70,12 @@ public interface ConfigApi {
@Parameter(description = "Expected request-params znode version")
int expectedParamsVersion);
- // TODO: Keep this regex in sync with GetConfig.MIGRATED_CONFIG_COMPONENTS
+ // 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)
@GET
-
@Path("/{component:updateHandler|directoryFactory|indexConfig|codecFactory}")
+ @Path(
+
"/{component:updateHandler|directoryFactory|indexConfig|codecFactory|query|searchComponents|queryResponseWriters}")
@StoreApiParameters
@Operation(
summary = "Fetch a config section",
@@ -80,5 +83,25 @@ public interface ConfigApi {
ConfigInfoResponse getConfigComponent(
@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)
+ @GET
+ @Path(
+
"/{component:updateHandler|directoryFactory|indexConfig|codecFactory|query|searchComponents|queryResponseWriters}/{componentName}")
+ @StoreApiParameters
+ @Operation(
+ summary = "Fetch a single named entry within a config section",
+ tags = {"config"})
+ ConfigInfoResponse getConfigComponentByName(
+ @PathParam("component") @Parameter(description = "Name of the config
component")
+ String component,
+ @PathParam("componentName") @Parameter(description = "Name of the
entry within the section")
+ String componentName,
+ @QueryParam("meta")
+ @DefaultValue("false")
+ @Parameter(description = "Include package metadata
(_packageinfo_)")
+ boolean meta);
}
}
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 8ea4dd3ca40..08614a0a4bc 100644
--- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
@@ -62,7 +62,6 @@ import
org.apache.solr.client.solrj.request.CollectionRequiringSolrRequest;
import org.apache.solr.client.solrj.response.SimpleSolrResponse;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.cloud.ZkSolrResourceLoader;
-import org.apache.solr.common.MapWriter;
import org.apache.solr.common.SolrErrorWrappingException;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.ClusterState;
@@ -77,7 +76,6 @@ import org.apache.solr.common.util.CommandOperation;
import org.apache.solr.common.util.EnvUtils;
import org.apache.solr.common.util.ExecutorUtil;
import org.apache.solr.common.util.NamedList;
-import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.common.util.SolrNamedThreadFactory;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.common.util.Utils;
@@ -92,8 +90,6 @@ import org.apache.solr.handler.admin.api.GetConfigAPI;
import org.apache.solr.handler.admin.api.ModifyConfigComponentAPI;
import org.apache.solr.handler.admin.api.ModifyParamSetAPI;
import org.apache.solr.handler.api.V2ApiUtils;
-import org.apache.solr.pkg.PackageAPI;
-import org.apache.solr.pkg.PackageListeners;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.SolrQueryResponse;
@@ -226,50 +222,22 @@ public class SolrConfigHandler extends RequestHandlerBase
req.getParams().getInt(ConfigOverlay.NAME, -1),
req.getParams().getInt(RequestParams.NAME, -1)));
} else if
(GetConfig.MIGRATED_CONFIG_COMPONENTS.contains(parts.get(1))) {
+ String componentName = req.getParams().get("componentName");
+ GetConfig getConfig = new GetConfig(req);
V2ApiUtils.squashIntoSolrResponseWithoutHeader(
- resp, new GetConfig(req).getConfigComponent(parts.get(1)));
+ resp,
+ componentName == null
+ ? getConfig.getConfigComponent(parts.get(1))
+ : getConfig.getConfigComponentByName(
+ parts.get(1), componentName,
req.getParams().getBool("meta", false)));
} else {
Map<String, Object> m = getConfigDetails(parts.get(1), req);
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) {
- @SuppressWarnings({"rawtypes"})
- Map pluginNameVsPluginInfo = (Map) val.get(parts.get(1));
- if (pluginNameVsPluginInfo != null) {
- Object o =
- pluginNameVsPluginInfo instanceof MapWriter
- ? pluginNameVsPluginInfo
- : pluginNameVsPluginInfo.get(componentName);
- Map<String, Object> pluginInfo =
- o instanceof MapWriter
- ? new SimpleOrderedMap<>((MapWriter) o)
- : (Map<String, Object>) o;
- val.put(
- parts.get(1),
- pluginNameVsPluginInfo instanceof MapWriter
- ? pluginInfo
- : Map.of(componentName, pluginInfo));
- if (req.getParams().getBool("meta", false)) {
- // meta=true is asking for the package info of the plugin
- // We go through all the listeners and see if there is one
registered for this
- // plugin
- List<PackageListeners.Listener> listeners =
- req.getCore().getPackageListeners().getListeners();
- for (PackageListeners.Listener listener : listeners) {
- Map<String, PackageAPI.PkgVersion> infos =
listener.packageDetails();
- if (infos == null || infos.isEmpty()) continue;
- infos.forEach(
- (s, mapWriter) -> {
- Map<String, Object> componentInfo =
- getComponentInfo(pluginInfo, componentName);
- if (s.equals(componentInfo.get("class"))) {
- (componentInfo).put("_packageinfo_", mapWriter);
- }
- });
- }
- }
- }
+ GetConfig.filterByComponentName(
+ val, parts.get(1), componentName,
req.getParams().getBool("meta", false), req);
}
resp.add("config", val);
}
@@ -277,16 +245,6 @@ public class SolrConfigHandler extends RequestHandlerBase
}
}
- @SuppressWarnings({"unchecked"})
- private Map<String, Object> getComponentInfo(
- Map<String, Object> pluginInfo, String componentName) {
- if (pluginInfo.containsKey("class")) {
- return pluginInfo;
- }
- return (Map<String, Object>)
- pluginInfo.computeIfAbsent(componentName, k -> new
LinkedHashMap<>());
- }
-
private Map<String, Object> getConfigDetails(String componentType,
SolrQueryRequest req) {
String componentName = componentType == null ? null :
req.getParams().get("componentName");
boolean showParams = req.getParams().getBool("expandParams", false);
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 806cd1b3971..1bc7db362bd 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
@@ -33,9 +33,12 @@ import org.apache.solr.client.api.endpoint.ConfigApi;
import org.apache.solr.client.api.model.ConfigInfoResponse;
import org.apache.solr.client.api.model.ConfigOverlayResponse;
import org.apache.solr.client.api.model.ConfigZnodeVersionResponse;
+import org.apache.solr.common.MapWriter;
+import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.PluginInfo;
@@ -43,16 +46,47 @@ import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.handler.SolrConfigHandler;
import org.apache.solr.jersey.NullKeyTolerantMap;
import org.apache.solr.jersey.PermissionName;
+import org.apache.solr.pkg.PackageAPI;
+import org.apache.solr.pkg.PackageListeners;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.util.SolrPluginUtils;
public class GetConfig extends JerseyResource implements ConfigApi.Get {
- // TODO: Keep this in sync with @Path regex on
ConfigApi.Get#getConfigComponent.
+ // TODO: Keep this in sync with @Path regex on
ConfigApi.Get#getConfigComponent. These are the
+ // internal SolrConfig section names (also used by the legacy v1 GET
/config/{component} path);
+ // see EXTERNAL_TO_INTERNAL_COMPONENT_NAME for the v2-only pluralized
renames.
// TODO: Remove this set when remaining GET /config/{component} sections
also migrate.
public static final Set<String> MIGRATED_CONFIG_COMPONENTS =
- Set.of("updateHandler", "directoryFactory", "indexConfig",
"codecFactory");
+ Set.of(
+ "updateHandler",
+ "directoryFactory",
+ "indexConfig",
+ "codecFactory",
+ "query",
+ "searchComponent",
+ "queryResponseWriter");
+
+ // v2-only renames: external (URL/response-key) component name -> internal
SolrConfig section
+ // name. v1's GET /config/{component} keeps using the internal names
unchanged.
+ private static final Map<String, String> EXTERNAL_TO_INTERNAL_COMPONENT_NAME
=
+ Map.of(
+ "searchComponents", "searchComponent",
+ "queryResponseWriters", "queryResponseWriter");
+
+ private static String toInternalComponentName(String externalName) {
+ return EXTERNAL_TO_INTERNAL_COMPONENT_NAME.getOrDefault(externalName,
externalName);
+ }
+
+ public static String toExternalComponentName(String internalName) {
+ for (Map.Entry<String, String> e :
EXTERNAL_TO_INTERNAL_COMPONENT_NAME.entrySet()) {
+ if (e.getValue().equals(internalName)) {
+ return e.getKey();
+ }
+ }
+ return internalName;
+ }
private final SolrQueryRequest solrQueryRequest;
@@ -97,14 +131,91 @@ public class GetConfig extends JerseyResource implements
ConfigApi.Get {
@Override
@PermissionName(CONFIG_READ_PERM)
- public ConfigInfoResponse getConfigComponent(String componentName) {
+ public ConfigInfoResponse getConfigComponent(String component) {
+ final var response = instantiateJerseyResponse(ConfigInfoResponse.class);
+ Map<String, Object> section = new LinkedHashMap<>();
+ section.put(component,
solrConfigAsMap().get(toInternalComponentName(component)));
+ response.config = jsonSafeCopy(section);
+ return response;
+ }
+
+ @Override
+ @PermissionName(CONFIG_READ_PERM)
+ public ConfigInfoResponse getConfigComponentByName(
+ String component, String componentName, boolean meta) {
final var response = instantiateJerseyResponse(ConfigInfoResponse.class);
Map<String, Object> section = new LinkedHashMap<>();
- section.put(componentName, solrConfigAsMap().get(componentName));
+ section.put(component,
solrConfigAsMap().get(toInternalComponentName(component)));
+ filterByComponentName(section, component, componentName, meta,
solrQueryRequest);
response.config = jsonSafeCopy(section);
return response;
}
+ /**
+ * Shared by the v2 {@code getConfigComponentByName} API and {@link
+ * org.apache.solr.handler.SolrConfigHandler}'s legacy v1 handling for
components not yet migrated
+ * to v2, so the two don't carry separate copies of this logic.
+ *
+ * @throws SolrException NOT_FOUND if {@code componentName} does not exist
within {@code
+ * component}
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public static void filterByComponentName(
+ Map<String, Object> section,
+ String component,
+ String componentName,
+ boolean meta,
+ SolrQueryRequest req) {
+ Map pluginNameVsPluginInfo = (Map) section.get(component);
+ if (pluginNameVsPluginInfo == null) {
+ return;
+ }
+
+ Object o =
+ pluginNameVsPluginInfo instanceof MapWriter
+ ? pluginNameVsPluginInfo
+ : pluginNameVsPluginInfo.get(componentName);
+ if (o == null) {
+ throw new SolrException(
+ SolrException.ErrorCode.NOT_FOUND,
+ componentName + " not found in config section " + component);
+ }
+
+ Map<String, Object> pluginInfo =
+ o instanceof MapWriter ? new SimpleOrderedMap<>((MapWriter) o) :
(Map<String, Object>) o;
+ section.put(
+ component,
+ pluginNameVsPluginInfo instanceof MapWriter
+ ? pluginInfo
+ : Map.of(componentName, pluginInfo));
+
+ if (meta) {
+ List<PackageListeners.Listener> listeners =
+ req.getCore().getPackageListeners().getListeners();
+ for (PackageListeners.Listener listener : listeners) {
+ Map<String, PackageAPI.PkgVersion> infos = listener.packageDetails();
+ if (infos == null || infos.isEmpty()) continue;
+ infos.forEach(
+ (s, mapWriter) -> {
+ Map<String, Object> componentInfo = getComponentInfo(pluginInfo,
componentName);
+ if (s.equals(componentInfo.get("class"))) {
+ (componentInfo).put("_packageinfo_", mapWriter);
+ }
+ });
+ }
+ }
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private static Map<String, Object> getComponentInfo(
+ Map<String, Object> pluginInfo, String componentName) {
+ if (pluginInfo.containsKey("class")) {
+ return pluginInfo;
+ }
+ return (Map<String, Object>)
+ pluginInfo.computeIfAbsent(componentName, k -> new LinkedHashMap<>());
+ }
+
private Map<String, Object> solrConfigAsMap() {
return Utils.convertToMap(solrQueryRequest.getCore().getSolrConfig(), new
LinkedHashMap<>());
}
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 1e67cb09c50..808bdf545f4 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
@@ -61,10 +61,7 @@ public class GetConfigAPI {
@EndPoint(
path = {
"/config/luceneMatchVersion",
- "/config/query",
"/config/requestHandler",
- "/config/searchComponent",
- "/config/queryResponseWriter",
"/config/initParams",
"/config/listener",
"/config/requestDispatcher",
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 252946a5971..6e0a2aa6e27 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
@@ -49,7 +49,7 @@ public class V2ConfigAPIMappingTest extends
V2ApiMappingTest<SolrConfigHandler>
// the API lookup works for a handful of the valid config "components".
@Test
public void testGetSingleComponentConfig() {
- assertAnnotatedApiExistsFor("GET", "/config/query");
+ assertAnnotatedApiExistsFor("GET", "/config/requestHandler");
assertAnnotatedApiExistsFor("GET", "/config/jmx");
assertAnnotatedApiExistsFor("GET", "/config/requestDispatcher");
assertAnnotatedApiExistsFor("GET", "/config/luceneMatchVersion");
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 7b34a37bbb1..19b29380afd 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
@@ -80,14 +80,15 @@ public class GetConfigSolrCloudTest extends
SolrCloudTestCase {
@Test
public void testGetConfigComponentFromCollection() throws Exception {
for (String component : GetConfig.MIGRATED_CONFIG_COMPONENTS) {
+ String pathComponent = GetConfig.toExternalComponentName(component);
var request =
- new ConfigApi.GetConfigComponent(IndexType.COLLECTION,
COLLECTION_NAME, component);
+ new ConfigApi.GetConfigComponent(IndexType.COLLECTION,
COLLECTION_NAME, pathComponent);
var response = request.process(cluster.getSolrClient());
assertNotNull(component, response);
assertNull(component, response.error);
assertNotNull(component, response.config);
- assertTrue(component, response.config.containsKey(component));
+ assertTrue(component, response.config.containsKey(pathComponent));
assertEquals(component, 1, response.config.size());
}
}
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 b0fbbfa8035..504fe7ecb5e 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
@@ -18,8 +18,10 @@ package org.apache.solr.handler.admin.api;
import static org.apache.solr.core.CoreContainer.ALLOW_PATHS_SYSPROP;
+import java.util.Map;
import org.apache.solr.SolrTestCase;
import org.apache.solr.client.api.model.IndexType;
+import org.apache.solr.client.solrj.RemoteSolrException;
import org.apache.solr.client.solrj.request.ConfigApi;
import org.apache.solr.util.ExternalPaths;
import org.apache.solr.util.SolrJettyTestRule;
@@ -84,14 +86,90 @@ public class GetConfigTest extends SolrTestCase {
@Test
public void testGetConfigComponentFromCore() throws Exception {
for (String component : GetConfig.MIGRATED_CONFIG_COMPONENTS) {
- var request = new ConfigApi.GetConfigComponent(IndexType.CORE,
CORE_NAME, component);
+ String pathComponent = GetConfig.toExternalComponentName(component);
+ var request = new ConfigApi.GetConfigComponent(IndexType.CORE,
CORE_NAME, pathComponent);
var response = request.process(solrTestRule.getSolrClient(CORE_NAME));
assertNotNull(component, response);
assertNull(component, response.error);
assertNotNull(component, response.config);
- assertTrue(component, response.config.containsKey(component));
+ assertTrue(component, response.config.containsKey(pathComponent));
assertEquals(component, 1, response.config.size());
}
}
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testGetConfigComponentFilteredByNameFromCore() throws Exception {
+ var request =
+ new ConfigApi.GetConfigComponentByName(
+ IndexType.CORE, CORE_NAME, "searchComponents", "spellcheck");
+ request.setMeta(true);
+ var response = request.process(solrTestRule.getSolrClient(CORE_NAME));
+
+ assertNotNull(response);
+ assertNull(response.error);
+ assertNotNull(response.config);
+ assertEquals(1, response.config.size());
+
+ Map<String, Object> section = (Map<String, Object>)
response.config.get("searchComponents");
+ assertNotNull(section);
+ assertTrue(section.containsKey("spellcheck"));
+
+ Map<String, Object> spellcheck = (Map<String, Object>)
section.get("spellcheck");
+ assertNotNull(spellcheck);
+ assertEquals("solr.SpellCheckComponent", spellcheck.get("class"));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testGetConfigComponentFilteredByNameOmitsPackageInfoByDefault()
throws Exception {
+ var request =
+ new ConfigApi.GetConfigComponentByName(
+ IndexType.CORE, CORE_NAME, "searchComponents", "spellcheck");
+ // meta is left unset, so it defaults to false and no package lookup
should happen.
+ var response = request.process(solrTestRule.getSolrClient(CORE_NAME));
+
+ Map<String, Object> section = (Map<String, Object>)
response.config.get("searchComponents");
+ Map<String, Object> spellcheck = (Map<String, Object>)
section.get("spellcheck");
+ assertFalse(spellcheck.containsKey("_packageinfo_"));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testGetConfigComponentFilteredByNameForOtherMigratedComponents()
throws Exception {
+ // filterCache is one of the few <query> entries the default configset
doesn't comment out.
+ var queryRequest =
+ new ConfigApi.GetConfigComponentByName(IndexType.CORE, CORE_NAME,
"query", "filterCache");
+ var queryResponse =
queryRequest.process(solrTestRule.getSolrClient(CORE_NAME));
+ assertNotNull(queryResponse);
+ assertNull(queryResponse.error);
+ Map<String, Object> querySection = (Map<String, Object>)
queryResponse.config.get("query");
+ assertNotNull(querySection);
+ assertTrue(querySection.containsKey("filterCache"));
+ }
+
+ @Test
+ public void testGetConfigComponentByUnknownNameReturnsNotFound() {
+ var request =
+ new ConfigApi.GetConfigComponentByName(
+ IndexType.CORE, CORE_NAME, "searchComponents", "doesNotExist");
+ final RemoteSolrException ex =
+ expectThrows(
+ RemoteSolrException.class,
+ () -> request.process(solrTestRule.getSolrClient(CORE_NAME)));
+ assertEquals(404, ex.code());
+ }
+
+ @Test
+ public void testGetConfigComponentRejectsOldSingularSearchComponentPath() {
+ // v2 intentionally renamed this one section to the plural
"searchComponents" in the URL;
+ // 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());
+ }
}
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 f01f882a0d0..38981e7caf6 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
@@ -119,6 +119,7 @@ To see the configuration for implicit request handlers, add
`expandParams=true`
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, `searchComponent` and `queryResponseWriter` are exposed under
the pluralized names `searchComponents` and `queryResponseWriters` instead.
To further restrict the request to a single component within a top-level
section, use the `componentName` request parameter.
@@ -161,6 +162,54 @@ The output of this command will look similar to:
The ability to restrict to objects within a top-level section is limited to
request handlers (`requestHandler`), search components (`searchComponent`), and
response writers (`queryResponseWriter`).
+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 example, to return configuration for the `elevator` search component:
+
+[tabs#getnamedcomponent]
+======
+V1 API::
++
+====
+[source,bash]
+----
+http://localhost:8983/solr/techproducts/config/searchComponent?componentName=elevator
+----
+====
+V2 API::
++
+====
+[source,bash]
+----
+http://localhost:8983/api/collections/techproducts/config/searchComponents/elevator
+----
+====
+======
+
+The same applies to response writers.
+For example, to return configuration for the `xslt` response writer:
+
+[tabs#getnamedwriter]
+======
+V1 API::
++
+====
+[source,bash]
+----
+http://localhost:8983/solr/techproducts/config/queryResponseWriter?componentName=xslt
+----
+====
+V2 API::
++
+====
+[source,bash]
+----
+http://localhost:8983/api/collections/techproducts/config/queryResponseWriters/xslt
+----
+====
+======
+
+Request handlers are unaffected and continue to use `componentName` in both
APIs, as shown above.
+
== Commands to Modify the Config
This API uses specific commands with POST requests to tell Solr what property
or type of property to add to or modify in `configoverlay.json`.