You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ge...@apache.org on 2023/06/01 13:10:05 UTC
[solr] branch main updated: SOLR-16392: Tweak v2 CREATESHARD to be more REST-ful (#1671)
This is an automated email from the ASF dual-hosted git repository.
gerlowskija 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 66d6a55895f SOLR-16392: Tweak v2 CREATESHARD to be more REST-ful (#1671)
66d6a55895f is described below
commit 66d6a55895fa51074e325d260bbdc14f8beb560a
Author: Jason Gerlowski <ge...@apache.org>
AuthorDate: Thu Jun 1 09:09:58 2023 -0400
SOLR-16392: Tweak v2 CREATESHARD to be more REST-ful (#1671)
The "create" command-specifier has been removed, but the API otherwise
remains unchanged.
This commit also converts the API over to the JAX-RS framework.
---
solr/CHANGES.txt | 3 +
.../solr/handler/admin/CollectionsHandler.java | 41 +---
.../solr/handler/admin/api/CreateShardAPI.java | 220 +++++++++++++++++----
.../solr/handler/admin/api/CreateShardAPITest.java | 172 ++++++++++++++++
.../handler/admin/api/V2ShardsAPIMappingTest.java | 41 ----
.../deployment-guide/pages/shard-management.adoc | 6 +-
6 files changed, 366 insertions(+), 117 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 511854d30c0..df3f81eebbb 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -147,6 +147,9 @@ Improvements
* SOLR-16816: Update node metrics while making affinityPlacement selections. Therefore selections can be made given the expected cluster
information after the previous selections are implemented. (Houston Putman)
+* SOLR-16392: The v2 "create shard" API has been tweaked to be more intuitive, by removing the top-level "create"
+ command specifier. The rest of the API remains unchanged. (Jason Gerlowski)
+
Optimizations
---------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
index e7bf5e92ec5..d00ed09b766 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
@@ -136,7 +136,6 @@ import org.apache.solr.client.solrj.impl.HttpSolrClient.Builder;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.CoreAdminRequest.RequestSyncShard;
import org.apache.solr.client.solrj.response.RequestStatusState;
-import org.apache.solr.client.solrj.util.SolrIdentifierValidator;
import org.apache.solr.cloud.OverseerSolrResponse;
import org.apache.solr.cloud.OverseerSolrResponseSerializer;
import org.apache.solr.cloud.OverseerTaskQueue;
@@ -152,8 +151,6 @@ import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.cloud.ClusterProperties;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
-import org.apache.solr.common.cloud.DocCollection.CollectionStateProps;
-import org.apache.solr.common.cloud.ImplicitDocRouter;
import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.Replica.State;
import org.apache.solr.common.cloud.Slice;
@@ -763,40 +760,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
CREATESHARD_OP(
CREATESHARD,
(req, rsp, h) -> {
- Map<String, Object> map =
- copy(req.getParams().required(), null, COLLECTION_PROP, SHARD_ID_PROP);
- ClusterState clusterState = h.coreContainer.getZkController().getClusterState();
- final String newShardName =
- SolrIdentifierValidator.validateShardName(req.getParams().get(SHARD_ID_PROP));
- boolean followAliases = req.getParams().getBool(FOLLOW_ALIASES, false);
- String extCollectionName = req.getParams().get(COLLECTION_PROP);
- String collectionName =
- followAliases
- ? h.coreContainer
- .getZkController()
- .getZkStateReader()
- .getAliases()
- .resolveSimpleAlias(extCollectionName)
- : extCollectionName;
- if (!ImplicitDocRouter.NAME.equals(
- ((Map<?, ?>)
- clusterState
- .getCollection(collectionName)
- .get(CollectionStateProps.DOC_ROUTER))
- .get(NAME)))
- throw new SolrException(
- ErrorCode.BAD_REQUEST, "shards can be added only to 'implicit' collections");
- copy(
- req.getParams(),
- map,
- REPLICATION_FACTOR,
- NRT_REPLICAS,
- TLOG_REPLICAS,
- PULL_REPLICAS,
- CREATE_NODE_SET,
- WAIT_FOR_FINAL_STATE,
- FOLLOW_ALIASES);
- return copyPropertiesWithPrefix(req.getParams(), map, PROPERTY_PREFIX);
+ CreateShardAPI.invokeFromV1Params(h.coreContainer, req, rsp);
+ return null;
}),
DELETEREPLICA_OP(
DELETEREPLICA,
@@ -1553,6 +1518,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
CreateAliasAPI.class,
CreateCollectionAPI.class,
CreateCollectionBackupAPI.class,
+ CreateShardAPI.class,
DeleteAliasAPI.class,
DeleteCollectionBackupAPI.class,
DeleteCollectionAPI.class,
@@ -1577,7 +1543,6 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
public Collection<Api> getApis() {
final List<Api> apis = new ArrayList<>();
apis.addAll(AnnotatedApi.getApis(new SplitShardAPI(this)));
- apis.addAll(AnnotatedApi.getApis(new CreateShardAPI(this)));
apis.addAll(AnnotatedApi.getApis(new AddReplicaAPI(this)));
apis.addAll(AnnotatedApi.getApis(new SyncShardAPI(this)));
apis.addAll(AnnotatedApi.getApis(new ForceLeaderAPI(this)));
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateShardAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateShardAPI.java
index 080068c4d69..3f39404f45a 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateShardAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateShardAPI.java
@@ -17,65 +17,215 @@
package org.apache.solr.handler.admin.api;
-import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
+import static org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2;
+import static org.apache.solr.cloud.Overseer.QUEUE_OPERATION;
+import static org.apache.solr.cloud.api.collections.CollectionHandlingUtils.CREATE_NODE_SET;
+import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
+import static org.apache.solr.common.cloud.ZkStateReader.NRT_REPLICAS;
+import static org.apache.solr.common.cloud.ZkStateReader.PULL_REPLICAS;
+import static org.apache.solr.common.cloud.ZkStateReader.REPLICATION_FACTOR;
+import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
+import static org.apache.solr.common.cloud.ZkStateReader.TLOG_REPLICAS;
import static org.apache.solr.common.params.CollectionAdminParams.COLLECTION;
import static org.apache.solr.common.params.CollectionAdminParams.CREATE_NODE_SET_PARAM;
+import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CollectionAdminParams.PROPERTY_PREFIX;
-import static org.apache.solr.common.params.CommonParams.ACTION;
-import static org.apache.solr.handler.ClusterAPI.wrapParams;
-import static org.apache.solr.handler.api.V2ApiUtils.flattenMapWithPrefix;
+import static org.apache.solr.common.params.CollectionAdminParams.SHARD;
+import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
+import static org.apache.solr.common.params.CommonAdminParams.WAIT_FOR_FINAL_STATE;
+import static org.apache.solr.common.params.CommonParams.NAME;
+import static org.apache.solr.handler.admin.api.CreateCollectionAPI.copyPrefixedPropertiesWithoutPrefix;
import static org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
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.CreateShardPayload;
+import javax.inject.Inject;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import org.apache.solr.client.solrj.util.SolrIdentifierValidator;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.ImplicitDocRouter;
+import org.apache.solr.common.cloud.ZkNodeProps;
import org.apache.solr.common.params.CollectionParams;
-import org.apache.solr.handler.admin.CollectionsHandler;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.handler.api.V2ApiUtils;
+import org.apache.solr.jersey.JacksonReflectMapWriter;
+import org.apache.solr.jersey.PermissionName;
+import org.apache.solr.jersey.SubResponseAccumulatingJerseyResponse;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
/**
* V2 API for creating a new shard in a collection.
*
- * <p>This API (POST /v2/collections/collectionName/shards {'create': {...}}) is analogous to the v1
+ * <p>This API (POST /v2/collections/collectionName/shards {...}) is analogous to the v1
* /admin/collections?action=CREATESHARD command.
- *
- * @see CreateShardAPI
*/
-@EndPoint(
- path = {"/c/{collection}/shards", "/collections/{collection}/shards"},
- method = POST,
- permission = COLL_EDIT_PERM)
-public class CreateShardAPI {
- private static final String V2_CREATE_CMD = "create";
+@Path("/collections/{collectionName}/shards")
+public class CreateShardAPI extends AdminAPIBase {
+
+ @Inject
+ public CreateShardAPI(
+ CoreContainer coreContainer,
+ SolrQueryRequest solrQueryRequest,
+ SolrQueryResponse solrQueryResponse) {
+ super(coreContainer, solrQueryRequest, solrQueryResponse);
+ }
- private final CollectionsHandler collectionsHandler;
+ @POST
+ @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2})
+ @PermissionName(COLL_EDIT_PERM)
+ public SubResponseAccumulatingJerseyResponse createShard(
+ @PathParam("collectionName") String collectionName, CreateShardRequestBody requestBody)
+ throws Exception {
+ final var response = instantiateJerseyResponse(SubResponseAccumulatingJerseyResponse.class);
+ if (requestBody == null) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST, "Required request-body is missing");
+ }
+ ensureRequiredParameterProvided(COLLECTION_PROP, collectionName);
+ ensureRequiredParameterProvided(SHARD_ID_PROP, requestBody.shardName);
+ SolrIdentifierValidator.validateShardName(requestBody.shardName);
+ final String resolvedCollectionName =
+ resolveAndValidateAliasIfEnabled(
+ collectionName, Boolean.TRUE.equals(requestBody.followAliases));
+ ensureCollectionUsesImplicitRouter(resolvedCollectionName);
- public CreateShardAPI(CollectionsHandler collectionsHandler) {
- this.collectionsHandler = collectionsHandler;
+ final ZkNodeProps remoteMessage = createRemoteMessage(resolvedCollectionName, requestBody);
+ submitRemoteMessageAndHandleResponse(
+ response,
+ CollectionParams.CollectionAction.CREATESHARD,
+ remoteMessage,
+ requestBody.asyncId);
+ return response;
}
- @Command(name = V2_CREATE_CMD)
- public void createShard(PayloadObj<CreateShardPayload> obj) throws Exception {
- final CreateShardPayload v2Body = obj.get();
- final Map<String, Object> v1Params = v2Body.toMap(new HashMap<>());
- v1Params.put(ACTION, CollectionParams.CollectionAction.CREATESHARD.toLower());
- v1Params.put(COLLECTION, obj.getRequest().getPathTemplateValues().get(COLLECTION));
+ public static class CreateShardRequestBody implements JacksonReflectMapWriter {
+ @JsonProperty(NAME)
+ public String shardName;
+
+ @JsonProperty(REPLICATION_FACTOR)
+ public Integer replicationFactor;
+
+ @JsonProperty(NRT_REPLICAS)
+ public Integer nrtReplicas;
+
+ @JsonProperty(TLOG_REPLICAS)
+ public Integer tlogReplicas;
+
+ @JsonProperty(PULL_REPLICAS)
+ public Integer pullReplicas;
+
+ @JsonProperty("createReplicas")
+ public Boolean createReplicas;
+
+ @JsonProperty("nodeSet")
+ public List<String> nodeSet;
+
+ @JsonProperty(WAIT_FOR_FINAL_STATE)
+ public Boolean waitForFinalState;
- if (v2Body.nodeSet != null) {
- v1Params.put(CREATE_NODE_SET_PARAM, buildV1CreateNodeSetValue(v2Body.nodeSet));
+ @JsonProperty(FOLLOW_ALIASES)
+ public Boolean followAliases;
+
+ @JsonProperty(ASYNC)
+ public String asyncId;
+
+ @JsonProperty public Map<String, String> properties;
+
+ public static CreateShardRequestBody fromV1Params(SolrParams params) {
+ params.required().check(COLLECTION, SHARD);
+
+ final var requestBody = new CreateShardRequestBody();
+ requestBody.shardName = params.get(SHARD);
+ requestBody.replicationFactor = params.getInt(REPLICATION_FACTOR);
+ requestBody.nrtReplicas = params.getInt(NRT_REPLICAS);
+ requestBody.tlogReplicas = params.getInt(TLOG_REPLICAS);
+ requestBody.pullReplicas = params.getInt(PULL_REPLICAS);
+ if (params.get(CREATE_NODE_SET_PARAM) != null) {
+ final String nodeSetStr = params.get(CREATE_NODE_SET_PARAM);
+ if ("EMPTY".equals(nodeSetStr)) {
+ requestBody.createReplicas = false;
+ } else {
+ requestBody.nodeSet = Arrays.asList(nodeSetStr.split(","));
+ }
+ }
+ requestBody.waitForFinalState = params.getBool(WAIT_FOR_FINAL_STATE);
+ requestBody.followAliases = params.getBool(FOLLOW_ALIASES);
+ requestBody.asyncId = params.get(ASYNC);
+ requestBody.properties =
+ copyPrefixedPropertiesWithoutPrefix(params, new HashMap<>(), PROPERTY_PREFIX);
+
+ return requestBody;
}
+ }
- flattenMapWithPrefix(v2Body.coreProperties, v1Params, PROPERTY_PREFIX);
- collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(), v1Params), obj.getResponse());
+ public static void invokeFromV1Params(
+ CoreContainer coreContainer,
+ SolrQueryRequest solrQueryRequest,
+ SolrQueryResponse solrQueryResponse)
+ throws Exception {
+ final var requestBody = CreateShardRequestBody.fromV1Params(solrQueryRequest.getParams());
+ final var createShardApi =
+ new CreateShardAPI(coreContainer, solrQueryRequest, solrQueryResponse);
+ final var response =
+ createShardApi.createShard(solrQueryRequest.getParams().get(COLLECTION), requestBody);
+ V2ApiUtils.squashIntoSolrResponseWithoutHeader(solrQueryResponse, response);
}
- private String buildV1CreateNodeSetValue(List<String> nodeSet) {
- if (nodeSet.size() > 0) {
- return String.join(",", nodeSet);
+ public static ZkNodeProps createRemoteMessage(
+ String collectionName, CreateShardRequestBody requestBody) {
+ final Map<String, Object> remoteMessage = new HashMap<>();
+ remoteMessage.put(QUEUE_OPERATION, CollectionParams.CollectionAction.CREATESHARD.toLower());
+ remoteMessage.put(COLLECTION_PROP, collectionName);
+ remoteMessage.put(SHARD_ID_PROP, requestBody.shardName);
+ if (requestBody.createReplicas == null || requestBody.createReplicas) {
+ // The remote message expects a single comma-delimited string, so nodeSet requires flattening
+ if (requestBody.nodeSet != null) {
+ remoteMessage.put(CREATE_NODE_SET_PARAM, String.join(",", requestBody.nodeSet));
+ }
+ } else {
+ remoteMessage.put(CREATE_NODE_SET, "EMPTY");
}
- return "EMPTY";
+ insertIfNotNull(remoteMessage, REPLICATION_FACTOR, requestBody.replicationFactor);
+ insertIfNotNull(remoteMessage, NRT_REPLICAS, requestBody.nrtReplicas);
+ insertIfNotNull(remoteMessage, TLOG_REPLICAS, requestBody.tlogReplicas);
+ insertIfNotNull(remoteMessage, PULL_REPLICAS, requestBody.pullReplicas);
+ insertIfNotNull(remoteMessage, WAIT_FOR_FINAL_STATE, requestBody.waitForFinalState);
+ insertIfNotNull(remoteMessage, FOLLOW_ALIASES, requestBody.followAliases);
+ insertIfNotNull(remoteMessage, ASYNC, requestBody.asyncId);
+
+ if (requestBody.properties != null) {
+ requestBody
+ .properties
+ .entrySet()
+ .forEach(
+ entry -> {
+ remoteMessage.put(PROPERTY_PREFIX + entry.getKey(), entry.getValue());
+ });
+ }
+
+ return new ZkNodeProps(remoteMessage);
+ }
+
+ private void ensureCollectionUsesImplicitRouter(String collectionName) {
+ final ClusterState clusterState = coreContainer.getZkController().getClusterState();
+ if (!ImplicitDocRouter.NAME.equals(
+ ((Map<?, ?>)
+ clusterState
+ .getCollection(collectionName)
+ .get(DocCollection.CollectionStateProps.DOC_ROUTER))
+ .get(NAME)))
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST,
+ "shards can be added only to 'implicit' collections");
}
}
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/CreateShardAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/CreateShardAPITest.java
new file mode 100644
index 00000000000..07b71d8a805
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/handler/admin/api/CreateShardAPITest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.admin.api;
+
+import static org.apache.solr.cloud.Overseer.QUEUE_OPERATION;
+import static org.apache.solr.common.cloud.ZkStateReader.NRT_REPLICAS;
+import static org.apache.solr.common.cloud.ZkStateReader.PULL_REPLICAS;
+import static org.apache.solr.common.cloud.ZkStateReader.REPLICATION_FACTOR;
+import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
+import static org.apache.solr.common.cloud.ZkStateReader.TLOG_REPLICAS;
+import static org.apache.solr.common.params.CollectionAdminParams.COLLECTION;
+import static org.apache.solr.common.params.CollectionAdminParams.CREATE_NODE_SET_PARAM;
+import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
+import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
+import static org.apache.solr.common.params.CommonAdminParams.WAIT_FOR_FINAL_STATE;
+import static org.hamcrest.Matchers.containsString;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.hamcrest.MatcherAssert;
+import org.junit.Test;
+
+/** Unit tests for {@link CreateShardAPI} */
+public class CreateShardAPITest extends SolrTestCaseJ4 {
+
+ @Test
+ public void testReportsErrorIfRequestBodyMissing() {
+ final SolrException thrown =
+ expectThrows(
+ SolrException.class,
+ () -> {
+ final var api = new CreateShardAPI(null, null, null);
+ api.createShard("someCollName", null);
+ });
+
+ assertEquals(400, thrown.code());
+ assertEquals("Required request-body is missing", thrown.getMessage());
+ }
+
+ @Test
+ public void testReportsErrorIfCollectionNameMissing() {
+ final var requestBody = new CreateShardAPI.CreateShardRequestBody();
+ requestBody.shardName = "someShardName";
+ final SolrException thrown =
+ expectThrows(
+ SolrException.class,
+ () -> {
+ final var api = new CreateShardAPI(null, null, null);
+ api.createShard(null, requestBody);
+ });
+
+ assertEquals(400, thrown.code());
+ assertEquals("Missing required parameter: collection", thrown.getMessage());
+ }
+
+ @Test
+ public void testReportsErrorIfShardNameMissing() {
+ final var requestBody = new CreateShardAPI.CreateShardRequestBody();
+ requestBody.shardName = null;
+ final SolrException thrown =
+ expectThrows(
+ SolrException.class,
+ () -> {
+ final var api = new CreateShardAPI(null, null, null);
+ api.createShard("someCollectionName", requestBody);
+ });
+
+ assertEquals(400, thrown.code());
+ assertEquals("Missing required parameter: shard", thrown.getMessage());
+ }
+
+ @Test
+ public void testReportsErrorIfShardNameIsInvalid() {
+ final var requestBody = new CreateShardAPI.CreateShardRequestBody();
+ requestBody.shardName = "invalid$shard@name";
+ final SolrException thrown =
+ expectThrows(
+ SolrException.class,
+ () -> {
+ final var api = new CreateShardAPI(null, null, null);
+ api.createShard("someCollectionName", requestBody);
+ });
+
+ assertEquals(400, thrown.code());
+ MatcherAssert.assertThat(
+ thrown.getMessage(), containsString("Invalid shard: [invalid$shard@name]"));
+ }
+
+ @Test
+ public void testCreateRemoteMessageAllProperties() {
+ final var requestBody = new CreateShardAPI.CreateShardRequestBody();
+ requestBody.shardName = "someShardName";
+ requestBody.replicationFactor = 123;
+ requestBody.nrtReplicas = 123;
+ requestBody.tlogReplicas = 456;
+ requestBody.pullReplicas = 789;
+ requestBody.createReplicas = true;
+ requestBody.nodeSet = List.of("node1", "node2");
+ requestBody.waitForFinalState = true;
+ requestBody.followAliases = true;
+ requestBody.asyncId = "someAsyncId";
+ requestBody.properties = Map.of("propName1", "propVal1", "propName2", "propVal2");
+
+ final var remoteMessage =
+ CreateShardAPI.createRemoteMessage("someCollectionName", requestBody).getProperties();
+
+ assertEquals(13, remoteMessage.size());
+ assertEquals("createshard", remoteMessage.get(QUEUE_OPERATION));
+ assertEquals("someCollectionName", remoteMessage.get(COLLECTION));
+ assertEquals("someShardName", remoteMessage.get(SHARD_ID_PROP));
+ assertEquals(123, remoteMessage.get(REPLICATION_FACTOR));
+ assertEquals(123, remoteMessage.get(NRT_REPLICAS));
+ assertEquals(456, remoteMessage.get(TLOG_REPLICAS));
+ assertEquals(789, remoteMessage.get(PULL_REPLICAS));
+ assertEquals("node1,node2", remoteMessage.get(CREATE_NODE_SET_PARAM));
+ assertEquals(true, remoteMessage.get(WAIT_FOR_FINAL_STATE));
+ assertEquals(true, remoteMessage.get(FOLLOW_ALIASES));
+ assertEquals("someAsyncId", remoteMessage.get(ASYNC));
+ assertEquals("propVal1", remoteMessage.get("property.propName1"));
+ assertEquals("propVal2", remoteMessage.get("property.propName2"));
+ }
+
+ @Test
+ public void testCanConvertV1ParamsToV2RequestBody() {
+ final var v1Params = new ModifiableSolrParams();
+ v1Params.add(COLLECTION, "someCollectionName");
+ v1Params.add(SHARD_ID_PROP, "someShardName");
+ v1Params.set(REPLICATION_FACTOR, 123);
+ v1Params.set(NRT_REPLICAS, 123);
+ v1Params.set(TLOG_REPLICAS, 456);
+ v1Params.set(PULL_REPLICAS, 789);
+ v1Params.add(CREATE_NODE_SET_PARAM, "node1,node2");
+ v1Params.set(WAIT_FOR_FINAL_STATE, true);
+ v1Params.set(FOLLOW_ALIASES, true);
+ v1Params.add(ASYNC, "someAsyncId");
+ v1Params.add("property.propName1", "propVal1");
+ v1Params.add("property.propName2", "propVal2");
+
+ final var requestBody = CreateShardAPI.CreateShardRequestBody.fromV1Params(v1Params);
+
+ assertEquals("someShardName", requestBody.shardName);
+ assertEquals(Integer.valueOf(123), requestBody.replicationFactor);
+ assertEquals(Integer.valueOf(123), requestBody.nrtReplicas);
+ assertEquals(Integer.valueOf(456), requestBody.tlogReplicas);
+ assertEquals(Integer.valueOf(789), requestBody.pullReplicas);
+ assertNull(requestBody.createReplicas);
+ assertEquals(List.of("node1", "node2"), requestBody.nodeSet);
+ assertEquals(Boolean.TRUE, requestBody.waitForFinalState);
+ assertEquals(Boolean.TRUE, requestBody.followAliases);
+ assertEquals("someAsyncId", requestBody.asyncId);
+ assertEquals("propVal1", requestBody.properties.get("propName1"));
+ assertEquals("propVal2", requestBody.properties.get("propName2"));
+ }
+}
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/V2ShardsAPIMappingTest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/V2ShardsAPIMappingTest.java
index af4fda1cd43..3d8d1448329 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/api/V2ShardsAPIMappingTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/api/V2ShardsAPIMappingTest.java
@@ -17,11 +17,7 @@
package org.apache.solr.handler.admin.api;
-import static org.apache.solr.common.cloud.ZkStateReader.NRT_REPLICAS;
-import static org.apache.solr.common.cloud.ZkStateReader.PULL_REPLICAS;
-import static org.apache.solr.common.cloud.ZkStateReader.REPLICATION_FACTOR;
import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
-import static org.apache.solr.common.cloud.ZkStateReader.TLOG_REPLICAS;
import static org.apache.solr.common.params.CollectionAdminParams.COLLECTION;
import static org.apache.solr.common.params.CollectionAdminParams.CREATE_NODE_SET_PARAM;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
@@ -59,7 +55,6 @@ public class V2ShardsAPIMappingTest extends V2ApiMappingTest<CollectionsHandler>
public void populateApiBag() {
final CollectionsHandler collectionsHandler = getRequestHandler();
apiBag.registerObject(new SplitShardAPI(collectionsHandler));
- apiBag.registerObject(new CreateShardAPI(collectionsHandler));
apiBag.registerObject(new AddReplicaAPI(collectionsHandler));
apiBag.registerObject(new SyncShardAPI(collectionsHandler));
apiBag.registerObject(new ForceLeaderAPI(collectionsHandler));
@@ -137,42 +132,6 @@ public class V2ShardsAPIMappingTest extends V2ApiMappingTest<CollectionsHandler>
assertEquals("bar1", v1Params.get("property.bar"));
}
- @Test
- public void testCreateShardAllProperties() throws Exception {
- final SolrParams v1Params =
- captureConvertedV1Params(
- "/collections/collName/shards",
- "POST",
- "{ 'create': {"
- + "'shard': 'shard1', "
- + "'nodeSet': ['foo', 'bar', 'baz'], "
- + "'followAliases': true, "
- + "'async': 'some_async_id', "
- + "'waitForFinalState': true, "
- + "'replicationFactor': 123, "
- + "'nrtReplicas': 456, "
- + "'tlogReplicas': 789, "
- + "'pullReplicas': 101, "
- + "'coreProperties': {"
- + " 'foo': 'foo1', "
- + " 'bar': 'bar1', "
- + "}}}");
-
- assertEquals(CollectionParams.CollectionAction.CREATESHARD.lowerName, v1Params.get(ACTION));
- assertEquals("collName", v1Params.get(COLLECTION));
- assertEquals("shard1", v1Params.get(SHARD_ID_PROP));
- assertEquals("foo,bar,baz", v1Params.get(CREATE_NODE_SET_PARAM));
- assertTrue(v1Params.getPrimitiveBool(FOLLOW_ALIASES));
- assertEquals("some_async_id", v1Params.get(ASYNC));
- assertTrue(v1Params.getPrimitiveBool(WAIT_FOR_FINAL_STATE));
- assertEquals(123, v1Params.getPrimitiveInt(REPLICATION_FACTOR));
- assertEquals(456, v1Params.getPrimitiveInt(NRT_REPLICAS));
- assertEquals(789, v1Params.getPrimitiveInt(TLOG_REPLICAS));
- assertEquals(101, v1Params.getPrimitiveInt(PULL_REPLICAS));
- assertEquals("foo1", v1Params.get("property.foo"));
- assertEquals("bar1", v1Params.get("property.bar"));
- }
-
@Test
public void testAddReplicaAllProperties() throws Exception {
final SolrParams v1Params =
diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/shard-management.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/shard-management.adoc
index a691743c1e5..c8548b573b9 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/pages/shard-management.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/shard-management.adoc
@@ -324,9 +324,7 @@ http://localhost:8983/solr/admin/collections?action=CREATESHARD&shard=newShardNa
----
curl -X POST http://localhost:8983/api/collections/techproducts/shards -H 'Content-Type: application/json' -d '
{
- "create":{
- "shard":"newShardName"
- }
+ "shard":"newShardName"
}
'
----
@@ -358,6 +356,8 @@ s|Required |Default: none
+
The name of the collection that includes the shard to be split.
+Provided as a query parameter in v1 requests, and as a path parameter for v2 requests.
+
`shard`::
+