gerlowskija commented on code in PR #1679:
URL: https://github.com/apache/solr/pull/1679#discussion_r1242393959


##########
solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java:
##########
@@ -970,27 +965,17 @@ public Map<String, Object> execute(
     ADDREPLICA_OP(
         ADDREPLICA,
         (req, rsp, h) -> {
-          Map<String, Object> props =
-              copy(
-                  req.getParams(),
-                  null,
-                  COLLECTION_PROP,
-                  "node",
-                  SHARD_ID_PROP,
-                  _ROUTE_,
-                  CoreAdminParams.NAME,
-                  INSTANCE_DIR,
-                  DATA_DIR,
-                  ULOG_DIR,
-                  REPLICA_TYPE,
-                  WAIT_FOR_FINAL_STATE,
-                  NRT_REPLICAS,
-                  TLOG_REPLICAS,
-                  PULL_REPLICAS,
-                  CREATE_NODE_SET,
-                  FOLLOW_ALIASES,
-                  SKIP_NODE_ASSIGNMENT);
-          return copyPropertiesWithPrefix(req.getParams(), props, 
PROPERTY_PREFIX);
+          final var params = req.getParams();

Review Comment:
   [I] Application logic for Solr's APIs has historically lived in the 
`handleRequestBody` method of a "RequestHandler" class.  Often, a single 
RequestHandler will cover many logically distinct operations, one of which will 
be selected by users at request time via a specific query parameter.
   
   `CollectionsHandler.handleRequestBody` (which corresponds to the 
`/admin/collections` v1 API) looks for a query parameter called `action` and 
then uses a switch-case to execute different functionality based on the 
provided value.
   
   The application logic for this particular API is relatively simple - Solr is 
taking the user-provided parameters and formatting them into a message that can 
be stored in a work queue and processed later by a worker called the 
"overseer".  It is this work-queue processing that actually creates the replica 
(see AddReplicaCmd for the "real" replica-creation code).
   
   In any case, the logic behind the API itself "just" formats the message and 
stores it in ZK.
   
   ----
   
   Note that the v1 codepath (i.e. this RequestHandler) calls the v2 codepath 
(i.e. the API class) under the hood.  Structuring the code this way ensures 
that the v2 codepath is kept up to date if there are any changes to the v1 or 
v2 APIs.  It also ensures that the v2 codepath is tested implicitly in Solr's 
tests, even when users are making v1 requests. 



##########
solr/core/src/java/org/apache/solr/handler/admin/api/CreateReplicaAPI.java:
##########
@@ -0,0 +1,202 @@
+/*
+ * 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.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.REPLICA_TYPE;
+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.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.CollectionAdminParams.SKIP_NODE_ASSIGNMENT;
+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.CoreAdminParams.DATA_DIR;
+import static org.apache.solr.common.params.CoreAdminParams.INSTANCE_DIR;
+import static org.apache.solr.common.params.CoreAdminParams.NAME;
+import static org.apache.solr.common.params.CoreAdminParams.NODE;
+import static org.apache.solr.common.params.CoreAdminParams.ULOG_DIR;
+import static org.apache.solr.common.params.ShardParams._ROUTE_;
+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 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.common.SolrException;
+import org.apache.solr.common.cloud.ZkNodeProps;
+import org.apache.solr.common.params.CollectionParams;
+import org.apache.solr.common.params.CoreAdminParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.CollectionUtil;
+import org.apache.solr.core.CoreContainer;
+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 adding a new replica to an existing shard.
+ *
+ * <p>This API (POST /v2/collections/cName/shards/sName/replicas {...}) is 
analogous to the v1
+ * /admin/collections?action=ADDREPLICA command.
+ */
+@Path("/collections/{collectionName}/shards/{shardName}/replicas")
+public class CreateReplicaAPI extends AdminAPIBase {

Review Comment:
   [I] This class extends 'AdminAPIBase', which abstracts out a lot of 
functionality shared among `CollectionsHandler` APIs, which usually operate by 
formatting a "message" about the work to be done and storing it on a work 
queue.  (See [this 
comment](https://github.com/apache/solr/pull/1679/files#diff-582348d44491dcb0ce1dfb169fb544e9e95620b2d0448eb1a1744f4e8dd5a349R968)
 in CollectionsHandler for more details.)



##########
solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java:
##########
@@ -1543,7 +1529,6 @@ public Collection<Class<? extends JerseyResource>> 
getJerseyResources() {
   public Collection<Api> getApis() {
     final List<Api> apis = new ArrayList<>();
     apis.addAll(AnnotatedApi.getApis(new SplitShardAPI(this)));
-    apis.addAll(AnnotatedApi.getApis(new AddReplicaAPI(this)));

Review Comment:
   [I] Solr uses the `getApis` method to register v2 APIs that used our older 
"homegrown" API framework.  Since we're switching the add-replica functionality 
from the "homegrown" framework to the JAX-RS framework with this PR, we delete 
the registration line here.



##########
solr/solr-ref-guide/modules/deployment-guide/pages/replica-management.adoc:
##########
@@ -73,12 +73,9 @@ 
http://localhost:8983/solr/admin/collections?action=ADDREPLICA&collection=techpr
 
 [source,bash]
 ----
-curl -X POST http://localhost:8983/api/collections/techproducts/shards -H 
'Content-Type: application/json' -d '
+curl -X POST 
http://localhost:8983/api/collections/techproducts/shards/shard1/replicas -H 
'Content-Type: application/json' -d '

Review Comment:
   [I] Of course, if the v2 API is changing, the ref-guide docs should be 
updated to reflect its new form.  This often involves updating the path in the 
v2 example (if there is one).  If no v2 API example exists, it'd be ideal (but 
I guess not strictly required) to add one using syntax similar to that found 
here.



##########
solr/core/src/java/org/apache/solr/handler/admin/api/AddReplicaAPI.java:
##########
@@ -1,72 +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.admin.api;
-
-import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
-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.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.security.PermissionNameProvider.Name.COLL_EDIT_PERM;
-
-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.AddReplicaPayload;
-import org.apache.solr.common.params.CollectionParams;
-import org.apache.solr.handler.admin.CollectionsHandler;
-
-/**
- * V2 API for adding a new replica to an existing shard.
- *
- * <p>This API (POST /v2/collections/collectionName/shards {'add-replica': 
{...}}) is analogous to
- * the v1 /admin/collections?action=ADDREPLICA command.
- *
- * @see AddReplicaPayload
- */
-@EndPoint(
-    path = {"/c/{collection}/shards", "/collections/{collection}/shards"},
-    method = POST,
-    permission = COLL_EDIT_PERM)
-public class AddReplicaAPI {

Review Comment:
   [I] Github shows this file as "deleted", but in reality it has been renamed 
to CreateReplicaAPI.  The rename isn't strictly necessary and makes the PR a 
little more confusing, but CreateSomethingAPI fits a little better with the 
naming convention used in the rest of our JAX-RS APIs.



##########
solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java:
##########
@@ -1514,6 +1499,7 @@ public Boolean registerV2() {
   @Override
   public Collection<Class<? extends JerseyResource>> getJerseyResources() {
     return List.of(
+        CreateReplicaAPI.class,

Review Comment:
   [I] Solr uses the `getJerseyResources` method to register JAX-RS APIs, so we 
add our "CreateReplicaAPI" class here.



##########
solr/CHANGES.txt:
##########
@@ -150,6 +150,10 @@ Improvements
 * 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)
 
+* SOLR-16392: The v2 "add-replica" API has been tweaked to be more intuitive, 
by removing the top-level command specifier and

Review Comment:
   [I] This CHANGES.txt file is used by the project both as a way to credit 
contributors, and as a way for Solr users to learn about changes that might 
impact them as they prepare to upgrade.
   
   Entries are often written by the merging committer close to merge-time.  
(Merge conflicts with this file are a common problem, so doing this last often 
saves headache.). But contributors are also welcome to write the CHANGES.txt 
entry for their change, if they'd wish.
   
   v2 API changes usually fit best in the "Improvements" section.  The trailing 
parenthesis section is intended to credit anyone who contributed on the ticket 
(with code, as a reviewer, etc.).  These are formatted as a single name in 
parenthesis (in the case of committers who merged their own code), or as 
`(Contributor1, Contributor2 via MergingCommitter)` in the more normal case of 
multiple contributors whose code was merged by a committer. 



##########
solr/core/src/test/org/apache/solr/handler/admin/api/CreateReplicaAPITest.java:
##########
@@ -0,0 +1,182 @@
+/*
+ * 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.REPLICA_TYPE;
+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.SKIP_NODE_ASSIGNMENT;
+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.CoreAdminParams.DATA_DIR;
+import static org.apache.solr.common.params.CoreAdminParams.INSTANCE_DIR;
+import static org.apache.solr.common.params.CoreAdminParams.NAME;
+import static org.apache.solr.common.params.CoreAdminParams.NODE;
+import static org.apache.solr.common.params.CoreAdminParams.ULOG_DIR;
+import static org.apache.solr.common.params.ShardParams._ROUTE_;
+
+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.junit.Test;
+
+/** Unit tests for {@link CreateReplicaAPI} */
+public class CreateReplicaAPITest extends SolrTestCaseJ4 {

Review Comment:
   [I] Testing for v2 APIs is a bit complicated.
   
   Using integration tests would be ideal, but it's not really feasible to 
create a v2 test for each case exercised by our existing test suite (which uses 
the v1 APIs in almost all cases, as there is currently no Java client for 
making v2 API requests).
   
   Instead, most of our v2 APIs aim at good-enough coverage by:
   
   1. Writing unit tests to test as much of the application and API logic as 
possible at a "low" level. (As seen in this class)
   2. Structuring the v1 code to use the v2 code implicitly (see the [PR 
comment 
here](https://github.com/apache/solr/pull/1679/files#diff-582348d44491dcb0ce1dfb169fb544e9e95620b2d0448eb1a1744f4e8dd5a349R968)
 for more specifics.). This structural approach gives us at least some 
confidence that the v2 code is correct, since existing v1 tests are ultimately 
relying on it as well.



##########
solr/core/src/java/org/apache/solr/handler/admin/api/CreateReplicaAPI.java:
##########
@@ -0,0 +1,202 @@
+/*
+ * 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.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.REPLICA_TYPE;
+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.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.CollectionAdminParams.SKIP_NODE_ASSIGNMENT;
+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.CoreAdminParams.DATA_DIR;
+import static org.apache.solr.common.params.CoreAdminParams.INSTANCE_DIR;
+import static org.apache.solr.common.params.CoreAdminParams.NAME;
+import static org.apache.solr.common.params.CoreAdminParams.NODE;
+import static org.apache.solr.common.params.CoreAdminParams.ULOG_DIR;
+import static org.apache.solr.common.params.ShardParams._ROUTE_;
+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 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.common.SolrException;
+import org.apache.solr.common.cloud.ZkNodeProps;
+import org.apache.solr.common.params.CollectionParams;
+import org.apache.solr.common.params.CoreAdminParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.CollectionUtil;
+import org.apache.solr.core.CoreContainer;
+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 adding a new replica to an existing shard.
+ *
+ * <p>This API (POST /v2/collections/cName/shards/sName/replicas {...}) is 
analogous to the v1
+ * /admin/collections?action=ADDREPLICA command.
+ */
+@Path("/collections/{collectionName}/shards/{shardName}/replicas")
+public class CreateReplicaAPI extends AdminAPIBase {
+
+  @Inject
+  public CreateReplicaAPI(
+      CoreContainer coreContainer,
+      SolrQueryRequest solrQueryRequest,
+      SolrQueryResponse solrQueryResponse) {
+    super(coreContainer, solrQueryRequest, solrQueryResponse);
+  }
+
+  @POST
+  @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2})
+  @PermissionName(COLL_EDIT_PERM)
+  public SubResponseAccumulatingJerseyResponse createReplica(
+      @PathParam("collectionName") String collectionName,
+      @PathParam("shardName") String shardName,
+      AddReplicaRequestBody 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, shardName);
+    final String resolvedCollectionName =
+        resolveAndValidateAliasIfEnabled(
+            collectionName, Boolean.TRUE.equals(requestBody.followAliases));
+
+    final ZkNodeProps remoteMessage =
+        createRemoteMessage(resolvedCollectionName, shardName, requestBody);
+    submitRemoteMessageAndHandleResponse(
+        response, CollectionParams.CollectionAction.ADDREPLICA, remoteMessage, 
requestBody.asyncId);
+    return response;
+  }
+
+  public static ZkNodeProps createRemoteMessage(
+      String collectionName, String shardName, AddReplicaRequestBody 
requestBody) {
+    final Map<String, Object> remoteMessage = new HashMap<>();
+    remoteMessage.put(QUEUE_OPERATION, 
CollectionParams.CollectionAction.ADDREPLICA.toLower());
+    remoteMessage.put(COLLECTION_PROP, collectionName);
+    remoteMessage.put(SHARD_ID_PROP, shardName);
+    insertIfNotNull(remoteMessage, CoreAdminParams.NAME, requestBody.name);
+    insertIfNotNull(remoteMessage, _ROUTE_, requestBody.route);
+    insertIfNotNull(remoteMessage, NODE, requestBody.node);
+    if (CollectionUtil.isNotEmpty(requestBody.nodeSet)) {
+      remoteMessage.put(CREATE_NODE_SET_PARAM, String.join(",", 
requestBody.nodeSet));
+    }
+    insertIfNotNull(remoteMessage, SKIP_NODE_ASSIGNMENT, 
requestBody.skipNodeAssignment);
+    insertIfNotNull(remoteMessage, INSTANCE_DIR, requestBody.instanceDir);
+    insertIfNotNull(remoteMessage, DATA_DIR, requestBody.dataDir);
+    insertIfNotNull(remoteMessage, ULOG_DIR, requestBody.ulogDir);
+    insertIfNotNull(remoteMessage, REPLICA_TYPE, requestBody.type);
+    insertIfNotNull(remoteMessage, WAIT_FOR_FINAL_STATE, 
requestBody.waitForFinalState);
+    insertIfNotNull(remoteMessage, NRT_REPLICAS, requestBody.nrtReplicas);
+    insertIfNotNull(remoteMessage, TLOG_REPLICAS, requestBody.tlogReplicas);
+    insertIfNotNull(remoteMessage, PULL_REPLICAS, requestBody.pullReplicas);
+    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);
+  }
+
+  public static class AddReplicaRequestBody implements JacksonReflectMapWriter 
{

Review Comment:
   [I] The RequestBody object here implements "JacksonReflectMapWriter", which 
allows Solr to deserialize it from a number of different input formats 
(particularly XML, JSON, and "javabin" - a custom binary format supported by 
Solr)
   
   Jackson annotations are used to designate properties that can be 
serialized/deserialized.  An annotation value only needs specified when the 
property name is expected to differ from the variable name.
   
   This class is roughly analogous with the "Payload" class used in v2 POST/PUT 
APIs the use the old "homegrown" framework.  If a Payload class exists, it can 
usually be copied here with only minor modification.



##########
solr/core/src/java/org/apache/solr/handler/admin/api/CreateReplicaAPI.java:
##########
@@ -0,0 +1,202 @@
+/*
+ * 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.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.REPLICA_TYPE;
+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.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.CollectionAdminParams.SKIP_NODE_ASSIGNMENT;
+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.CoreAdminParams.DATA_DIR;
+import static org.apache.solr.common.params.CoreAdminParams.INSTANCE_DIR;
+import static org.apache.solr.common.params.CoreAdminParams.NAME;
+import static org.apache.solr.common.params.CoreAdminParams.NODE;
+import static org.apache.solr.common.params.CoreAdminParams.ULOG_DIR;
+import static org.apache.solr.common.params.ShardParams._ROUTE_;
+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 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.common.SolrException;
+import org.apache.solr.common.cloud.ZkNodeProps;
+import org.apache.solr.common.params.CollectionParams;
+import org.apache.solr.common.params.CoreAdminParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.CollectionUtil;
+import org.apache.solr.core.CoreContainer;
+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 adding a new replica to an existing shard.
+ *
+ * <p>This API (POST /v2/collections/cName/shards/sName/replicas {...}) is 
analogous to the v1
+ * /admin/collections?action=ADDREPLICA command.
+ */
+@Path("/collections/{collectionName}/shards/{shardName}/replicas")
+public class CreateReplicaAPI extends AdminAPIBase {
+
+  @Inject

Review Comment:
   [I] Solr's integration with the JAX-RS framework allows us to inject objects 
into the class ctor.  (While this can be changed - the framework currently 
instantiates a new CreateReplicaAPI instance for each incoming request.)
   
   Currently the types available for injection are relatively limited, but this 
can grow over time as needed.  See `InjectionFactories` and 
`JerseyApplications` for a better sense on the types currently available for 
injection, and for ideas on how to support injection of additional types.



##########
solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/AddReplicaPayload.java:
##########
@@ -1,56 +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.solrj.request.beans;
-
-import java.util.List;
-import java.util.Map;
-import org.apache.solr.common.annotation.JsonProperty;
-import org.apache.solr.common.util.ReflectMapWriter;
-
-public class AddReplicaPayload implements ReflectMapWriter {

Review Comment:
   [I] Payload classes were used by the "homegrown" annotation framework that 
we're in the process of moving away from, and have largely been superceded by 
the (very similar) "RequestBody" classes used by JAX-RS APIs.
   
   Very few changes are needed to affect this; see 
CreateReplicaAPI.CreateReplicaAPIRequestBody for more info. 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to