Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions changelog/unreleased/SOLR-16396-config-remaining-get-sections.yml
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,10 @@ ConfigInfoResponse getRequestHandler(
@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",
Expand All @@ -105,12 +103,15 @@ 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)
// 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",
Expand Down
13 changes: 3 additions & 10 deletions solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -237,16 +237,9 @@ private void handleGET() {
: 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));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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.
Expand Down Expand Up @@ -239,10 +268,16 @@ public static void filterByComponentName(
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
Expand Down Expand Up @@ -335,7 +370,7 @@ private Map<String, Object> requestHandlersWithImplicits(
return reqHandlersMap;
}

@SuppressWarnings({"unchecked", "rawtypes"})
@SuppressWarnings("unchecked")
private Map<String, Object> expandUseParams(SolrQueryRequest req, Object plugin) {
Map<String, Object> pluginInfo;
if (plugin instanceof Map) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@
/**
* 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;
Expand All @@ -54,37 +53,4 @@ public void getAllParamSets(SolrQueryRequest req, SolrQueryResponse rsp) {
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,6 @@ public void populateApiBag() {
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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
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;
Expand Down Expand Up @@ -242,6 +243,37 @@ public void testGetConfigComponentFilteredByNameForOtherMigratedComponents() thr
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 =
Expand All @@ -255,14 +287,42 @@ public void testGetConfigComponentByUnknownNameReturnsNotFound() {
}

@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());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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]
Expand All @@ -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]
Expand All @@ -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::
Expand All @@ -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

Expand Down
Loading