From fb6d76c9339c8f3b9a3388e9ffcbd3b7992b5389 Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Fri, 3 Jan 2025 07:01:15 -0500 Subject: [PATCH] SOLR-16396: Convert v2 configset APIs to JAX-RS (#2928) Convert v2 configset APIs to JAX-RS. Naturally this adds the APIs to Solr's growing v2 OAS, and ensures the APIs are included in code-gen artifacts. Also makes slight tweaks to line APIs up with the more REST-ful design chosen for other v2 APIs. - 'clone' (i.e. 'create-from-existing') is now available at `POST /api/configsets {...}` - 'delete' is now available at `DELETE /api/configsets/csName` - 'list' is now available at `GET /api/configsets` --- solr/CHANGES.txt | 4 + .../client/api/endpoint/ConfigsetsApi.java | 100 +++++++++++++++++ .../api/endpoint/ListConfigsetsApi.java | 32 ------ .../api/model/CloneConfigsetRequestBody.java} | 15 +-- .../solr/handler/admin/ConfigSetsHandler.java | 101 ++++++----------- ...eConfigSetAPI.java => CloneConfigSet.java} | 62 ++++++----- .../handler/configsets/ConfigSetAPIBase.java | 17 ++- ...ConfigSetAPI.java => DeleteConfigSet.java} | 39 ++++--- .../handler/configsets/ListConfigSets.java | 6 +- ...ConfigSetAPI.java => UploadConfigSet.java} | 103 ++++++++++++++---- .../configsets/UploadConfigSetFileAPI.java | 96 ---------------- .../apache/solr/cloud/TestConfigSetsAPI.java | 8 +- .../configsets/ListConfigSetsAPITest.java | 2 +- solr/packaging/test/test_zk.bats | 2 +- .../pages/configsets-api.adoc | 40 +++---- 15 files changed, 315 insertions(+), 312 deletions(-) create mode 100644 solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java delete mode 100644 solr/api/src/java/org/apache/solr/client/api/endpoint/ListConfigsetsApi.java rename solr/{solrj/src/java/org/apache/solr/client/solrj/request/beans/CreateConfigPayload.java => api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java} (70%) rename solr/core/src/java/org/apache/solr/handler/configsets/{CreateConfigSetAPI.java => CloneConfigSet.java} (53%) rename solr/core/src/java/org/apache/solr/handler/configsets/{DeleteConfigSetAPI.java => DeleteConfigSet.java} (64%) rename solr/core/src/java/org/apache/solr/handler/configsets/{UploadConfigSetAPI.java => UploadConfigSet.java} (53%) delete mode 100644 solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetFileAPI.java diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index ec96b6aac2e..ee8943015e2 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -15,6 +15,10 @@ Improvements specific collections or cores. Collection information can be fetched by a call to `GET /api/collections/collectionName`, and core information with a call to `GET /api/cores/coreName/segments`. (Jason Gerlowski) +* SOLR-16396: All v2 configset APIs have been moved to the slightly different path: `/api/configsets`, to better align with the design of + other v2 APIs. SolrJ now offers (experimental) SolrRequest implementations for all v2 configset APIs in + `org.apache.solr.client.solrj.request.ConfigsetsApi`. (Jason Gerlowski) + Optimizations --------------------- * SOLR-17578: Remove ZkController internal core supplier, for slightly faster reconnection after Zookeeper session loss. (Pierre Salagnac) diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java new file mode 100644 index 00000000000..9961b4c9f28 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.client.api.endpoint; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.QueryParam; +import java.io.IOException; +import java.io.InputStream; +import org.apache.solr.client.api.model.CloneConfigsetRequestBody; +import org.apache.solr.client.api.model.ListConfigsetsResponse; +import org.apache.solr.client.api.model.SolrJerseyResponse; + +public interface ConfigsetsApi { + + /** V2 API definition for listing the configsets available to this SolrCloud cluster. */ + @Path("/configsets") + interface List { + @GET + @Operation( + summary = "List the configsets available to Solr.", + tags = {"configsets"}) + ListConfigsetsResponse listConfigSet() throws Exception; + } + + /** + * V2 API definition for creating a (possibly slightly modified) copy of an existing configset + * + *

Equivalent to the existing v1 API /admin/configs?action=CREATE + */ + @Path("/configsets") + interface Clone { + @POST + @Operation( + summary = "Create a new configset modeled on an existing one.", + tags = {"configsets"}) + SolrJerseyResponse cloneExistingConfigSet(CloneConfigsetRequestBody requestBody) + throws Exception; + } + + /** + * V2 API definition for deleting an existing configset. + * + *

Equivalent to the existing v1 API /admin/configs?action=DELETE + */ + @Path("/configsets/{configSetName}") + interface Delete { + @DELETE + @Operation(summary = "Delete an existing configset.", tags = "configsets") + SolrJerseyResponse deleteConfigSet(@PathParam("configSetName") String configSetName) + throws Exception; + } + + /** + * V2 API definitions for uploading a configset, in whole or part. + * + *

Equivalent to the existing v1 API /admin/configs?action=UPLOAD + */ + @Path("/configsets/{configSetName}") + interface Upload { + @PUT + @Operation(summary = "Create a new configset.", tags = "configsets") + SolrJerseyResponse uploadConfigSet( + @PathParam("configSetName") String configSetName, + @QueryParam("overwrite") Boolean overwrite, + @QueryParam("cleanup") Boolean cleanup, + @RequestBody(required = true) InputStream requestBody) + throws IOException; + + @PUT + @Path("{filePath:.+}") + SolrJerseyResponse uploadConfigSetFile( + @PathParam("configSetName") String configSetName, + @PathParam("filePath") String filePath, + @QueryParam("overwrite") Boolean overwrite, + @QueryParam("cleanup") Boolean cleanup, + @RequestBody(required = true) InputStream requestBody) + throws IOException; + } +} diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/ListConfigsetsApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/ListConfigsetsApi.java deleted file mode 100644 index 7e0cf620b7f..00000000000 --- a/solr/api/src/java/org/apache/solr/client/api/endpoint/ListConfigsetsApi.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.solr.client.api.endpoint; - -import io.swagger.v3.oas.annotations.Operation; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import org.apache.solr.client.api.model.ListConfigsetsResponse; - -/** V2 API definition for listing configsets. */ -@Path("/cluster/configs") -public interface ListConfigsetsApi { - @GET - @Operation( - summary = "List the configsets available to Solr.", - tags = {"configsets"}) - ListConfigsetsResponse listConfigSet() throws Exception; -} diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/CreateConfigPayload.java b/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java similarity index 70% rename from solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/CreateConfigPayload.java rename to solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java index 5f7f2e6687d..14e22225986 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/CreateConfigPayload.java +++ b/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java @@ -14,19 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.solr.client.solrj.request.beans; +package org.apache.solr.client.api.model; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Map; -import org.apache.solr.common.annotation.JsonProperty; -import org.apache.solr.common.util.ReflectMapWriter; -public class CreateConfigPayload implements ReflectMapWriter { - public static final String DEFAULT_CONFIGSET = - "_default"; // TODO Better location for this in SolrJ? +/** Request body for ConfigsetsApi.Clone */ +public class CloneConfigsetRequestBody { + public static final String DEFAULT_CONFIGSET = "_default"; @JsonProperty(required = true) public String name; - @JsonProperty public String baseConfigSet = DEFAULT_CONFIGSET; + @JsonProperty(defaultValue = DEFAULT_CONFIGSET) + public String baseConfigSet; + @JsonProperty public Map properties; } diff --git a/solr/core/src/java/org/apache/solr/handler/admin/ConfigSetsHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/ConfigSetsHandler.java index ff69b1ee147..535deb54e44 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/ConfigSetsHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/ConfigSetsHandler.java @@ -17,36 +17,30 @@ package org.apache.solr.handler.admin; import static org.apache.solr.common.params.CommonParams.NAME; -import static org.apache.solr.handler.configsets.UploadConfigSetFileAPI.FILEPATH_PLACEHOLDER; import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.apache.solr.api.AnnotatedApi; import org.apache.solr.api.Api; import org.apache.solr.api.JerseyResource; -import org.apache.solr.api.PayloadObj; -import org.apache.solr.client.solrj.request.beans.CreateConfigPayload; +import org.apache.solr.client.api.model.CloneConfigsetRequestBody; +import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.cloud.ConfigSetCmds; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.params.ConfigSetParams; import org.apache.solr.common.params.ConfigSetParams.ConfigSetAction; -import org.apache.solr.common.params.DefaultSolrParams; -import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.core.CoreContainer; import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.handler.api.V2ApiUtils; -import org.apache.solr.handler.configsets.CreateConfigSetAPI; -import org.apache.solr.handler.configsets.DeleteConfigSetAPI; +import org.apache.solr.handler.configsets.CloneConfigSet; +import org.apache.solr.handler.configsets.ConfigSetAPIBase; +import org.apache.solr.handler.configsets.DeleteConfigSet; import org.apache.solr.handler.configsets.ListConfigSets; -import org.apache.solr.handler.configsets.UploadConfigSetAPI; -import org.apache.solr.handler.configsets.UploadConfigSetFileAPI; -import org.apache.solr.request.DelegatingSolrQueryRequest; +import org.apache.solr.handler.configsets.UploadConfigSet; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.security.AuthorizationContext; @@ -96,51 +90,30 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw switch (action) { case DELETE: - final DeleteConfigSetAPI deleteConfigSetAPI = new DeleteConfigSetAPI(coreContainer); - final SolrQueryRequest v2DeleteReq = - new DelegatingSolrQueryRequest(req) { - @Override - public Map getPathTemplateValues() { - return Map.of( - DeleteConfigSetAPI.CONFIGSET_NAME_PLACEHOLDER, - req.getParams().required().get(NAME)); - } - }; - deleteConfigSetAPI.deleteConfigSet(v2DeleteReq, rsp); + final DeleteConfigSet deleteConfigSetAPI = new DeleteConfigSet(coreContainer, req, rsp); + final var deleteResponse = + deleteConfigSetAPI.deleteConfigSet(req.getParams().required().get(NAME)); + V2ApiUtils.squashIntoSolrResponseWithoutHeader(rsp, deleteResponse); break; case UPLOAD: - final SolrQueryRequest v2UploadReq = - new DelegatingSolrQueryRequest(req) { - @Override - public Map getPathTemplateValues() { - final Map templateValsByName = new HashMap<>(); - - templateValsByName.put( - UploadConfigSetAPI.CONFIGSET_NAME_PLACEHOLDER, - req.getParams().required().get(NAME)); - if (!req.getParams().get(ConfigSetParams.FILE_PATH, "").isEmpty()) { - templateValsByName.put( - FILEPATH_PLACEHOLDER, req.getParams().get(ConfigSetParams.FILE_PATH)); - } - return templateValsByName; - } - - // Set the v1 default vals where they differ from v2's - @Override - public SolrParams getParams() { - final ModifiableSolrParams v1Defaults = new ModifiableSolrParams(); - v1Defaults.add(ConfigSetParams.OVERWRITE, "false"); - v1Defaults.add(ConfigSetParams.CLEANUP, "false"); - return new DefaultSolrParams(super.getParams(), v1Defaults); - } - }; + final var uploadApi = new UploadConfigSet(coreContainer, req, rsp); + final var configSetName = req.getParams().required().get(NAME); + final var overwrite = req.getParams().getBool(ConfigSetParams.OVERWRITE, false); + final var cleanup = req.getParams().getBool(ConfigSetParams.CLEANUP, false); + final var configSetData = ConfigSetAPIBase.ensureNonEmptyInputStream(req); + SolrJerseyResponse uploadResponse; if (req.getParams() .get(ConfigSetParams.FILE_PATH, "") .isEmpty()) { // Uploading a whole configset - new UploadConfigSetAPI(coreContainer).uploadConfigSet(v2UploadReq, rsp); + uploadResponse = + uploadApi.uploadConfigSet(configSetName, overwrite, cleanup, configSetData); } else { // Uploading a single file - new UploadConfigSetFileAPI(coreContainer).updateConfigSetFile(v2UploadReq, rsp); + final var filePath = req.getParams().get(ConfigSetParams.FILE_PATH); + uploadResponse = + uploadApi.uploadConfigSetFile( + configSetName, filePath, overwrite, cleanup, configSetData); } + V2ApiUtils.squashIntoSolrResponseWithoutHeader(rsp, uploadResponse); break; case LIST: final ListConfigSets listConfigSetsAPI = new ListConfigSets(coreContainer); @@ -153,12 +126,14 @@ public SolrParams getParams() { } // Map v1 parameters into v2 format and process request - final CreateConfigPayload createPayload = new CreateConfigPayload(); - createPayload.name = newConfigSetName; + final var requestBody = new CloneConfigsetRequestBody(); + requestBody.name = newConfigSetName; if (req.getParams().get(ConfigSetCmds.BASE_CONFIGSET) != null) { - createPayload.baseConfigSet = req.getParams().get(ConfigSetCmds.BASE_CONFIGSET); + requestBody.baseConfigSet = req.getParams().get(ConfigSetCmds.BASE_CONFIGSET); + } else { + requestBody.baseConfigSet = "_default"; } - createPayload.properties = new HashMap<>(); + requestBody.properties = new HashMap<>(); req.getParams().stream() .filter(entry -> entry.getKey().startsWith(ConfigSetCmds.CONFIG_SET_PROPERTY_PREFIX)) .forEach( @@ -167,10 +142,11 @@ public SolrParams getParams() { entry.getKey().substring(ConfigSetCmds.CONFIG_SET_PROPERTY_PREFIX.length()); final Object value = (entry.getValue().length == 1) ? entry.getValue()[0] : entry.getValue(); - createPayload.properties.put(newKey, value); + requestBody.properties.put(newKey, value); }); - final CreateConfigSetAPI createConfigSetAPI = new CreateConfigSetAPI(coreContainer); - createConfigSetAPI.create(new PayloadObj<>("create", null, createPayload, req, rsp)); + final CloneConfigSet createConfigSetAPI = new CloneConfigSet(coreContainer, req, rsp); + final var createResponse = createConfigSetAPI.cloneExistingConfigSet(requestBody); + V2ApiUtils.squashIntoSolrResponseWithoutHeader(rsp, createResponse); break; default: throw new IllegalStateException("Unexpected ConfigSetAction detected: " + action); @@ -207,18 +183,13 @@ public Boolean registerV2() { @Override public Collection getApis() { - final List apis = new ArrayList<>(); - apis.addAll(AnnotatedApi.getApis(new CreateConfigSetAPI(coreContainer))); - apis.addAll(AnnotatedApi.getApis(new DeleteConfigSetAPI(coreContainer))); - apis.addAll(AnnotatedApi.getApis(new UploadConfigSetAPI(coreContainer))); - apis.addAll(AnnotatedApi.getApis(new UploadConfigSetFileAPI(coreContainer))); - - return apis; + return new ArrayList<>(); } @Override public Collection> getJerseyResources() { - return List.of(ListConfigSets.class); + return List.of( + ListConfigSets.class, CloneConfigSet.class, DeleteConfigSet.class, UploadConfigSet.class); } @Override diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/CreateConfigSetAPI.java b/solr/core/src/java/org/apache/solr/handler/configsets/CloneConfigSet.java similarity index 53% rename from solr/core/src/java/org/apache/solr/handler/configsets/CreateConfigSetAPI.java rename to solr/core/src/java/org/apache/solr/handler/configsets/CloneConfigSet.java index 796903ff73c..e55c74e04fa 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/CreateConfigSetAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/CloneConfigSet.java @@ -17,54 +17,55 @@ package org.apache.solr.handler.configsets; -import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST; import static org.apache.solr.common.params.CommonParams.NAME; import static org.apache.solr.handler.admin.ConfigSetsHandler.DISABLE_CREATE_AUTH_CHECKS; import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM; +import jakarta.inject.Inject; import java.util.HashMap; import java.util.Map; -import org.apache.solr.api.Command; -import org.apache.solr.api.EndPoint; -import org.apache.solr.api.PayloadObj; -import org.apache.solr.client.solrj.request.beans.CreateConfigPayload; +import org.apache.solr.client.api.endpoint.ConfigsetsApi; +import org.apache.solr.client.api.model.CloneConfigsetRequestBody; +import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.cloud.ConfigSetCmds; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.ConfigSetParams; import org.apache.solr.core.CoreContainer; +import org.apache.solr.jersey.PermissionName; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; -/** - * V2 API for creating a new configset as a copy of an existing one. - * - *

This API (POST /v2/cluster/configs {"create": {...}}) is analogous to the v1 - * /admin/configs?action=CREATE command. - */ -@EndPoint(method = POST, path = "/cluster/configs", permission = CONFIG_EDIT_PERM) -public class CreateConfigSetAPI extends ConfigSetAPIBase { +/** V2 API implementation for ConfigsetsApi.Clone */ +public class CloneConfigSet extends ConfigSetAPIBase implements ConfigsetsApi.Clone { - public CreateConfigSetAPI(CoreContainer coreContainer) { - super(coreContainer); + @Inject + public CloneConfigSet( + CoreContainer coreContainer, + SolrQueryRequest solrQueryRequest, + SolrQueryResponse solrQueryResponse) { + super(coreContainer, solrQueryRequest, solrQueryResponse); } - @Command(name = "create") - public void create(PayloadObj obj) throws Exception { - final CreateConfigPayload createConfigPayload = obj.get(); - if (configSetService.checkConfigExists(createConfigPayload.name)) { + @Override + @PermissionName(CONFIG_EDIT_PERM) + public SolrJerseyResponse cloneExistingConfigSet(CloneConfigsetRequestBody requestBody) + throws Exception { + final var response = instantiateJerseyResponse(SolrJerseyResponse.class); + if (configSetService.checkConfigExists(requestBody.name)) { throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, - "ConfigSet already exists: " + createConfigPayload.name); + SolrException.ErrorCode.BAD_REQUEST, "ConfigSet already exists: " + requestBody.name); } // is there a base config that already exists - if (!configSetService.checkConfigExists(createConfigPayload.baseConfigSet)) { + if (!configSetService.checkConfigExists(requestBody.baseConfigSet)) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, - "Base ConfigSet does not exist: " + createConfigPayload.baseConfigSet); + "Base ConfigSet does not exist: " + requestBody.baseConfigSet); } if (!DISABLE_CREATE_AUTH_CHECKS - && !isTrusted(obj.getRequest().getUserPrincipal(), coreContainer.getAuthenticationPlugin()) - && configSetService.isConfigSetTrusted(createConfigPayload.baseConfigSet)) { + && !isTrusted(solrQueryRequest.getUserPrincipal(), coreContainer.getAuthenticationPlugin()) + && configSetService.isConfigSetTrusted(requestBody.baseConfigSet)) { throw new SolrException( SolrException.ErrorCode.UNAUTHORIZED, "Can't create a configset with an unauthenticated request from a trusted " @@ -72,16 +73,17 @@ public void create(PayloadObj obj) throws Exception { } final Map configsetCommandMsg = new HashMap<>(); - configsetCommandMsg.put(NAME, createConfigPayload.name); - configsetCommandMsg.put(ConfigSetCmds.BASE_CONFIGSET, createConfigPayload.baseConfigSet); - if (createConfigPayload.properties != null) { - for (Map.Entry e : createConfigPayload.properties.entrySet()) { + configsetCommandMsg.put(NAME, requestBody.name); + configsetCommandMsg.put(ConfigSetCmds.BASE_CONFIGSET, requestBody.baseConfigSet); + if (requestBody.properties != null) { + for (Map.Entry e : requestBody.properties.entrySet()) { configsetCommandMsg.put( ConfigSetCmds.CONFIG_SET_PROPERTY_PREFIX + e.getKey(), e.getValue()); } } runConfigSetCommand( - obj.getResponse(), ConfigSetParams.ConfigSetAction.CREATE, configsetCommandMsg); + solrQueryResponse, ConfigSetParams.ConfigSetAction.CREATE, configsetCommandMsg); + return response; } } diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java b/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java index 3f401e31bd8..f6e99167f30 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java @@ -28,6 +28,7 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; +import org.apache.solr.api.JerseyResource; import org.apache.solr.client.solrj.SolrResponse; import org.apache.solr.cloud.OverseerSolrResponseSerializer; import org.apache.solr.cloud.OverseerTaskQueue; @@ -53,18 +54,26 @@ *

Contains utilities for tasks common in configset manipulation, including running configset * "commands" and checking configset "trusted-ness". */ -public class ConfigSetAPIBase { +public class ConfigSetAPIBase extends JerseyResource { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); protected final CoreContainer coreContainer; + + protected final SolrQueryRequest solrQueryRequest; + protected final SolrQueryResponse solrQueryResponse; protected final Optional distributedCollectionConfigSetCommandRunner; - protected final ConfigSetService configSetService; - public ConfigSetAPIBase(CoreContainer coreContainer) { + public ConfigSetAPIBase( + CoreContainer coreContainer, + SolrQueryRequest solrQueryRequest, + SolrQueryResponse solrQueryResponse) { this.coreContainer = coreContainer; + this.solrQueryRequest = solrQueryRequest; + this.solrQueryResponse = solrQueryResponse; + this.distributedCollectionConfigSetCommandRunner = coreContainer.getDistributedCollectionCommandRunner(); this.configSetService = coreContainer.getConfigSetService(); @@ -96,7 +105,7 @@ protected void ensureConfigSetUploadEnabled() { } } - protected InputStream ensureNonEmptyInputStream(SolrQueryRequest req) throws IOException { + public static InputStream ensureNonEmptyInputStream(SolrQueryRequest req) throws IOException { Iterator contentStreamsIterator = req.getContentStreams().iterator(); if (!contentStreamsIterator.hasNext()) { diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSetAPI.java b/solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSet.java similarity index 64% rename from solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSetAPI.java rename to solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSet.java index 4867dd160fd..1a4b363a833 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSetAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSet.java @@ -16,40 +16,37 @@ */ package org.apache.solr.handler.configsets; -import static org.apache.solr.client.solrj.SolrRequest.METHOD.DELETE; import static org.apache.solr.common.params.CommonParams.NAME; import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM; +import jakarta.inject.Inject; import java.util.HashMap; import java.util.Map; -import org.apache.solr.api.EndPoint; +import org.apache.solr.client.api.endpoint.ConfigsetsApi; +import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.ConfigSetParams; import org.apache.solr.common.util.StrUtils; import org.apache.solr.core.CoreContainer; +import org.apache.solr.jersey.PermissionName; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; -/** - * V2 API for deleting an existing configset - * - *

This API (DELETE /v2/cluster/configs/configsetName) is analogous to the v1 - * /admin/configs?action=DELETE command. - */ -public class DeleteConfigSetAPI extends ConfigSetAPIBase { - - public static final String CONFIGSET_NAME_PLACEHOLDER = "name"; +/** V2 API implementation for ConfigsetsApi.Delete */ +public class DeleteConfigSet extends ConfigSetAPIBase implements ConfigsetsApi.Delete { - public DeleteConfigSetAPI(CoreContainer coreContainer) { - super(coreContainer); + @Inject + public DeleteConfigSet( + CoreContainer coreContainer, + SolrQueryRequest solrQueryRequest, + SolrQueryResponse solrQueryResponse) { + super(coreContainer, solrQueryRequest, solrQueryResponse); } - @EndPoint( - method = DELETE, - path = "/cluster/configs/{" + CONFIGSET_NAME_PLACEHOLDER + "}", - permission = CONFIG_EDIT_PERM) - public void deleteConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - final String configSetName = req.getPathTemplateValues().get("name"); + @Override + @PermissionName(CONFIG_EDIT_PERM) + public SolrJerseyResponse deleteConfigSet(String configSetName) throws Exception { + final var response = instantiateJerseyResponse(SolrJerseyResponse.class); if (StrUtils.isNullOrEmpty(configSetName)) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "No configset name provided to delete"); @@ -57,6 +54,8 @@ public void deleteConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws final Map configsetCommandMsg = new HashMap<>(); configsetCommandMsg.put(NAME, configSetName); - runConfigSetCommand(rsp, ConfigSetParams.ConfigSetAction.DELETE, configsetCommandMsg); + runConfigSetCommand( + solrQueryResponse, ConfigSetParams.ConfigSetAction.DELETE, configsetCommandMsg); + return response; } } diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/ListConfigSets.java b/solr/core/src/java/org/apache/solr/handler/configsets/ListConfigSets.java index 5f5d28adcfc..5b45fa38f22 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/ListConfigSets.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/ListConfigSets.java @@ -22,7 +22,7 @@ import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.HttpHeaders; import org.apache.solr.api.JerseyResource; -import org.apache.solr.client.api.endpoint.ListConfigsetsApi; +import org.apache.solr.client.api.endpoint.ConfigsetsApi; import org.apache.solr.client.api.model.ListConfigsetsResponse; import org.apache.solr.core.CoreContainer; import org.apache.solr.jersey.PermissionName; @@ -30,9 +30,9 @@ /** * V2 API implementation for listing all available configsets. * - *

This API (GET /v2/cluster/configs) is analogous to the v1 /admin/configs?action=LIST command. + *

This API (GET /v2/configsets) is analogous to the v1 /admin/configs?action=LIST command. */ -public class ListConfigSets extends JerseyResource implements ListConfigsetsApi { +public class ListConfigSets extends JerseyResource implements ConfigsetsApi.List { @Context public HttpHeaders headers; diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetAPI.java b/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSet.java similarity index 53% rename from solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetAPI.java rename to solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSet.java index 79d1b34d5ca..d42adf94999 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSet.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -16,9 +16,9 @@ */ package org.apache.solr.handler.configsets; -import static org.apache.solr.client.solrj.SolrRequest.METHOD.PUT; import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM; +import jakarta.inject.Inject; import java.io.IOException; import java.io.InputStream; import java.lang.invoke.MethodHandles; @@ -27,46 +27,47 @@ import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import org.apache.solr.api.EndPoint; +import org.apache.solr.client.api.endpoint.ConfigsetsApi; +import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.common.SolrException; -import org.apache.solr.common.params.ConfigSetParams; +import org.apache.solr.common.cloud.ZkMaintenanceUtils; import org.apache.solr.core.ConfigSetService; import org.apache.solr.core.CoreContainer; +import org.apache.solr.jersey.PermissionName; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.util.FileTypeMagicUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** - * V2 API for uploading a new configset (or overwriting an existing one). - * - *

This API (PUT /v2/cluster/configs/configsetName) is analogous to the v1 - * /admin/configs?action=UPLOAD command. - */ -public class UploadConfigSetAPI extends ConfigSetAPIBase { - - public static final String CONFIGSET_NAME_PLACEHOLDER = "name"; +public class UploadConfigSet extends ConfigSetAPIBase implements ConfigsetsApi.Upload { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - public UploadConfigSetAPI(CoreContainer coreContainer) { - super(coreContainer); + @Inject + public UploadConfigSet( + CoreContainer coreContainer, + SolrQueryRequest solrQueryRequest, + SolrQueryResponse solrQueryResponse) { + super(coreContainer, solrQueryRequest, solrQueryResponse); } - @EndPoint(method = PUT, path = "/cluster/configs/{name}", permission = CONFIG_EDIT_PERM) - public void uploadConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + @Override + @PermissionName(CONFIG_EDIT_PERM) + public SolrJerseyResponse uploadConfigSet( + String configSetName, Boolean overwrite, Boolean cleanup, InputStream requestBody) + throws IOException { + final var response = instantiateJerseyResponse(SolrJerseyResponse.class); ensureConfigSetUploadEnabled(); - final String configSetName = req.getPathTemplateValues().get("name"); boolean overwritesExisting = configSetService.checkConfigExists(configSetName); boolean requestIsTrusted = - isTrusted(req.getUserPrincipal(), coreContainer.getAuthenticationPlugin()); + isTrusted(solrQueryRequest.getUserPrincipal(), coreContainer.getAuthenticationPlugin()); // Get upload parameters - boolean allowOverwrite = req.getParams().getBool(ConfigSetParams.OVERWRITE, true); - boolean cleanup = req.getParams().getBool(ConfigSetParams.CLEANUP, false); - final InputStream inputStream = ensureNonEmptyInputStream(req); + if (overwrite == null) overwrite = true; + if (cleanup == null) cleanup = false; - if (overwritesExisting && !allowOverwrite) { + if (overwritesExisting && !overwrite) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "The configuration " + configSetName + " already exists"); @@ -84,7 +85,7 @@ public void uploadConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws // singleFilePath is not passed. createBaseNode(configSetService, overwritesExisting, requestIsTrusted, configSetName); - try (ZipInputStream zis = new ZipInputStream(inputStream, StandardCharsets.UTF_8)) { + try (ZipInputStream zis = new ZipInputStream(requestBody, StandardCharsets.UTF_8)) { boolean hasEntry = false; ZipEntry zipEntry; while ((zipEntry = zis.getNextEntry()) != null) { @@ -111,6 +112,60 @@ public void uploadConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws && !configSetService.isConfigSetTrusted(configSetName)) { configSetService.setConfigSetTrust(configSetName, true); } + return response; + } + + @Override + @PermissionName(CONFIG_EDIT_PERM) + public SolrJerseyResponse uploadConfigSetFile( + String configSetName, + String filePath, + Boolean overwrite, + Boolean cleanup, + InputStream requestBody) + throws IOException { + final var response = instantiateJerseyResponse(SolrJerseyResponse.class); + ensureConfigSetUploadEnabled(); + + boolean overwritesExisting = configSetService.checkConfigExists(configSetName); + boolean requestIsTrusted = + isTrusted(solrQueryRequest.getUserPrincipal(), coreContainer.getAuthenticationPlugin()); + + // Get upload parameters + + String singleFilePath = filePath != null ? filePath : ""; + if (overwrite == null) overwrite = true; + if (cleanup == null) cleanup = false; + + String fixedSingleFilePath = singleFilePath; + if (fixedSingleFilePath.charAt(0) == '/') { + fixedSingleFilePath = fixedSingleFilePath.substring(1); + } + byte[] data = requestBody.readAllBytes(); + if (fixedSingleFilePath.isEmpty()) { + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, + "The file path provided for upload, '" + singleFilePath + "', is not valid."); + } else if (ZkMaintenanceUtils.isFileForbiddenInConfigSets(fixedSingleFilePath) + || FileTypeMagicUtil.isFileForbiddenInConfigset(data)) { + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, + "The file type provided for upload, '" + + singleFilePath + + "', is forbidden for use in configSets."); + } else if (cleanup) { + // Cleanup is not allowed while using singleFilePath upload + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, + "ConfigSet uploads do not allow cleanup=true when file path is used."); + } else { + // Create a node for the configuration in config + // For creating the baseNode, the cleanup parameter is only allowed to be true when + // singleFilePath is not passed. + createBaseNode(configSetService, overwritesExisting, requestIsTrusted, configSetName); + configSetService.uploadFileToConfig(configSetName, fixedSingleFilePath, data, overwrite); + } + return response; } private void deleteUnusedFiles( diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetFileAPI.java b/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetFileAPI.java deleted file mode 100644 index 2380a79a92b..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetFileAPI.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.solr.handler.configsets; - -import static org.apache.solr.client.solrj.SolrRequest.METHOD.PUT; -import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM; - -import java.io.InputStream; -import org.apache.solr.api.EndPoint; -import org.apache.solr.common.SolrException; -import org.apache.solr.common.cloud.ZkMaintenanceUtils; -import org.apache.solr.common.params.ConfigSetParams; -import org.apache.solr.core.CoreContainer; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.util.FileTypeMagicUtil; - -/** - * V2 API for adding or updating a single file within a configset. - * - *

This API (PUT /v2/cluster/configs/configsetName/someFilePath) is analogous to the v1 - * /admin/configs?action=UPLOAD&filePath=someFilePath command. - */ -public class UploadConfigSetFileAPI extends ConfigSetAPIBase { - - public static final String CONFIGSET_NAME_PLACEHOLDER = - UploadConfigSetAPI.CONFIGSET_NAME_PLACEHOLDER; - public static final String FILEPATH_PLACEHOLDER = "*"; - - private static final String API_PATH = - "/cluster/configs/{" + CONFIGSET_NAME_PLACEHOLDER + "}/" + FILEPATH_PLACEHOLDER; - - public UploadConfigSetFileAPI(CoreContainer coreContainer) { - super(coreContainer); - } - - @EndPoint(method = PUT, path = API_PATH, permission = CONFIG_EDIT_PERM) - public void updateConfigSetFile(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - ensureConfigSetUploadEnabled(); - - final String configSetName = req.getPathTemplateValues().get("name"); - boolean overwritesExisting = configSetService.checkConfigExists(configSetName); - boolean requestIsTrusted = - isTrusted(req.getUserPrincipal(), coreContainer.getAuthenticationPlugin()); - - // Get upload parameters - - String singleFilePath = req.getPathTemplateValues().getOrDefault(FILEPATH_PLACEHOLDER, ""); - boolean allowOverwrite = req.getParams().getBool(ConfigSetParams.OVERWRITE, true); - boolean cleanup = req.getParams().getBool(ConfigSetParams.CLEANUP, false); - final InputStream inputStream = ensureNonEmptyInputStream(req); - - String fixedSingleFilePath = singleFilePath; - if (fixedSingleFilePath.charAt(0) == '/') { - fixedSingleFilePath = fixedSingleFilePath.substring(1); - } - byte[] data = inputStream.readAllBytes(); - if (fixedSingleFilePath.isEmpty()) { - throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, - "The file path provided for upload, '" + singleFilePath + "', is not valid."); - } else if (ZkMaintenanceUtils.isFileForbiddenInConfigSets(fixedSingleFilePath) - || FileTypeMagicUtil.isFileForbiddenInConfigset(data)) { - throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, - "The file type provided for upload, '" - + singleFilePath - + "', is forbidden for use in configSets."); - } else if (cleanup) { - // Cleanup is not allowed while using singleFilePath upload - throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, - "ConfigSet uploads do not allow cleanup=true when file path is used."); - } else { - // Create a node for the configuration in config - // For creating the baseNode, the cleanup parameter is only allowed to be true when - // singleFilePath is not passed. - createBaseNode(configSetService, overwritesExisting, requestIsTrusted, configSetName); - configSetService.uploadFileToConfig(configSetName, fixedSingleFilePath, data, allowOverwrite); - } - } -} diff --git a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java index eb28fdced07..54681b10e2c 100644 --- a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java +++ b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java @@ -1588,7 +1588,7 @@ private long uploadGivenConfigSet( final ByteBuffer fileBytes = TestSolrConfigHandler.getFileContent(file.getAbsolutePath(), false); final String uriEnding = - "/cluster/configs/" + "/configsets/" + configSetName + suffix + (!overwrite ? "?overwrite=false" : "") @@ -1639,11 +1639,13 @@ private long uploadSingleConfigSetFile( final ByteBuffer sampleConfigFile = TestSolrConfigHandler.getFileContent(file.getAbsolutePath(), false); + if (uploadPath != null && !uploadPath.startsWith("/")) { + uploadPath = "/" + uploadPath; + } final String uriEnding = - "/cluster/configs/" + "/configsets/" + configSetName + suffix - + "/" + uploadPath + (!overwrite ? "?overwrite=false" : "") + (cleanup ? "?cleanup=true" : ""); diff --git a/solr/core/src/test/org/apache/solr/handler/configsets/ListConfigSetsAPITest.java b/solr/core/src/test/org/apache/solr/handler/configsets/ListConfigSetsAPITest.java index 40100be48ac..776d0800e03 100644 --- a/solr/core/src/test/org/apache/solr/handler/configsets/ListConfigSetsAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/configsets/ListConfigSetsAPITest.java @@ -61,7 +61,7 @@ public void testSuccessfulListConfigsets() throws Exception { } /** - * Test the v2 to v1 response mapping for /cluster/configs + * Test the v2 to v1 response mapping for GET /configsets * *

{@link org.apache.solr.handler.admin.ConfigSetsHandler} uses {@link ListConfigSets} (and its * response class {@link ListConfigsetsResponse}) internally to serve the v1 version of this diff --git a/solr/packaging/test/test_zk.bats b/solr/packaging/test/test_zk.bats index ea9a372328a..447e1338cfa 100644 --- a/solr/packaging/test/test_zk.bats +++ b/solr/packaging/test/test_zk.bats @@ -135,7 +135,7 @@ teardown() { refute_output --partial "ERROR" sleep 1 - run curl "http://localhost:${SOLR_PORT}/api/cluster/configs?omitHeader=true" + run curl "http://localhost:${SOLR_PORT}/api/configsets" assert_output --partial '"configSets":["_default","techproducts2"]' } diff --git a/solr/solr-ref-guide/modules/configuration-guide/pages/configsets-api.adoc b/solr/solr-ref-guide/modules/configuration-guide/pages/configsets-api.adoc index 45987271de6..d021eb4440a 100644 --- a/solr/solr-ref-guide/modules/configuration-guide/pages/configsets-api.adoc +++ b/solr/solr-ref-guide/modules/configuration-guide/pages/configsets-api.adoc @@ -35,7 +35,7 @@ NOTE: This API can only be used with Solr running in SolrCloud mode. If you are not running Solr in SolrCloud mode but would still like to use shared configurations, please see the section xref:config-sets.adoc[]. The API works by passing commands to the `configs` endpoint. -The path to the endpoint varies depending on the API being used: the v1 API uses `solr/admin/configs`, while the v2 API uses `api/cluster/configs`. +The path to the endpoint varies depending on the API being used: the v1 API uses `/solr/admin/configs`, while the v2 API uses `/api/configsets`. Examples of both types are provided below. [[configsets-list]] @@ -64,7 +64,7 @@ With the v2 API, the `list` command is implied when there is no data sent with t [source,bash] ---- -http://localhost:8983/api/cluster/configs?omitHeader=true +http://localhost:8983/api/configsets?omitHeader=true ---- ==== ====== @@ -183,7 +183,7 @@ With the v2 API, the name of the configset to upload is provided as a path param $ (cd solr/server/solr/configsets/sample_techproducts_configs/conf && zip -r - *) > myconfigset.zip $ curl -X PUT --header "Content-Type:application/octet-stream" --data-binary @myconfigset.zip - "http://localhost:8983/api/cluster/configs/myConfigSet" + "http://localhost:8983/api/configsets/myConfigSet" ---- With this API, the default behavior is to overwrite the configset if it already exists. @@ -213,14 +213,14 @@ V2 API:: + ==== With the v2 API, the name of the configset and file are both provided in the URL. -They can be substituted in `/cluster/configs/__config_name__/__file_name__`. +They can be substituted in `/configsets/__config_name__/__file_name__`. The filename may be nested and include `/` characters. [source,bash] ---- curl -X PUT --header "Content-Type:application/octet-stream" --data-binary @solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml - "http://localhost:8983/api/cluster/configs/myConfigSet/solrconfig.xml" + "http://localhost:8983/api/configsets/myConfigSet/solrconfig.xml" ---- With this API, the default behavior is to overwrite the file if it already exists within the configset. @@ -282,30 +282,18 @@ http://localhost:8983/solr/admin/configs?action=CREATE&name=myConfigSet&baseConf V2 API:: + ==== -With the v2 API, the `create` command is provided as part of the JSON data that contains the required parameters: +With the v2 API, the `create` command is implicit and parameters are specified in a `POST` request body. [source,bash] ---- curl -X POST -H 'Content-type: application/json' -d '{ - "create":{ - "name": "myConfigSet", - "baseConfigSet": "predefinedTemplate", - "configSetProp.immutable": "false"}}' - http://localhost:8983/api/cluster/configs?omitHeader=true ----- - -With the v2 API, configset properties can also be provided via the `properties` map: - -[source,bash] ----- -curl -X POST -H 'Content-type: application/json' -d '{ - "create":{ - "name": "myConfigSet", - "baseConfigSet": "predefinedTemplate", - "properties": { - "immutable": "false" - }}}' - http://localhost:8983/api/cluster/configs?omitHeader=true + "name": "myConfigSet", + "baseConfigSet": "predefinedTemplate", + "properties": { + "immutable": "false" + } +}' + http://localhost:8983/api/configsets?omitHeader=true ---- ==== ====== @@ -361,7 +349,7 @@ The name of the configset to delete is provided as a path parameter: [source,bash] ---- -curl -X DELETE http://localhost:8983/api/cluster/configs/myConfigSet?omitHeader=true +curl -X DELETE http://localhost:8983/api/configsets/myConfigSet?omitHeader=true ---- ==== ======