This is an automated email from the ASF dual-hosted git repository. gerlowskija pushed a commit to branch SOLR-16825-migrate-definitions-to-api-module-pt4 in repository https://gitbox.apache.org/repos/asf/solr.git
commit 0f68b36ab5a4181932db2f942387bf0c871bac60 Author: Jason Gerlowski <[email protected]> AuthorDate: Tue Sep 26 16:30:15 2023 -0400 Migrade add-replica to 'api' module --- .../solr/client/api/endpoint/CreateReplicaApi.java | 44 +++++++++ .../client/api/model/CreateReplicaRequestBody.java | 48 ++++++++++ .../solr/handler/admin/CollectionsHandler.java | 9 +- .../{CreateReplicaAPI.java => CreateReplica.java} | 105 +++++++-------------- .../handler/admin/api/CreateReplicaAPITest.java | 23 ++--- .../solr/jersey/PostRequestLoggingFilterTest.java | 6 +- 6 files changed, 144 insertions(+), 91 deletions(-) diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/CreateReplicaApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/CreateReplicaApi.java new file mode 100644 index 00000000000..21c26ea8f49 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/CreateReplicaApi.java @@ -0,0 +1,44 @@ +/* + * 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 javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import org.apache.solr.client.api.model.CreateReplicaRequestBody; +import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse; + +/** + * V2 API definition 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 interface CreateReplicaApi { + + @POST + @Operation( + summary = "Creates a new replica of an existing shard.", + tags = {"replicas"}) + SubResponseAccumulatingJerseyResponse createReplica( + @PathParam("collectionName") String collectionName, + @PathParam("shardName") String shardName, + CreateReplicaRequestBody requestBody) + throws Exception; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CreateReplicaRequestBody.java b/solr/api/src/java/org/apache/solr/client/api/model/CreateReplicaRequestBody.java new file mode 100644 index 00000000000..a669cdaf414 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/CreateReplicaRequestBody.java @@ -0,0 +1,48 @@ +/* + * 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.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import java.util.Map; + +public class CreateReplicaRequestBody { + @JsonProperty public String name; + @JsonProperty public String type; // TODO Make this an enum - see SOLR-15796 + @JsonProperty public String instanceDir; + @JsonProperty public String dataDir; + @JsonProperty public String ulogDir; + @JsonProperty public String route; + @JsonProperty public Integer nrtReplicas; + @JsonProperty public Integer tlogReplicas; + @JsonProperty public Integer pullReplicas; + @JsonProperty public Boolean waitForFinalState; + @JsonProperty public Boolean followAliases; + + @JsonProperty public String async; + + // TODO This cluster of properties could probably be simplified down to just "nodeSet". See + // SOLR-15542 + @JsonProperty public String node; + + @JsonProperty("nodeSet") + public List<String> nodeSet; + + @JsonProperty public Boolean skipNodeAssignment; + + @JsonProperty public Map<String, String> properties; +} 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 a3ebbf4ae95..5231393ea4e 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 @@ -175,7 +175,7 @@ import org.apache.solr.handler.admin.api.CreateAliasAPI; import org.apache.solr.handler.admin.api.CreateCollectionAPI; import org.apache.solr.handler.admin.api.CreateCollectionBackupAPI; import org.apache.solr.handler.admin.api.CreateCollectionSnapshotAPI; -import org.apache.solr.handler.admin.api.CreateReplicaAPI; +import org.apache.solr.handler.admin.api.CreateReplica; import org.apache.solr.handler.admin.api.CreateShardAPI; import org.apache.solr.handler.admin.api.DeleteAlias; import org.apache.solr.handler.admin.api.DeleteCollection; @@ -942,9 +942,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission final var params = req.getParams(); params.required().check(COLLECTION_PROP, SHARD_ID_PROP); - final var api = new CreateReplicaAPI(h.coreContainer, req, rsp); - final var requestBody = - CreateReplicaAPI.AddReplicaRequestBody.fromV1Params(req.getParams()); + final var api = new CreateReplica(h.coreContainer, req, rsp); + final var requestBody = CreateReplica.createRequestBodyFromV1Params(req.getParams()); final var response = api.createReplica( params.get(COLLECTION_PROP), params.get(SHARD_ID_PROP), requestBody); @@ -1362,7 +1361,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission @Override public Collection<Class<? extends JerseyResource>> getJerseyResources() { return List.of( - CreateReplicaAPI.class, + CreateReplica.class, AddReplicaProperty.class, BalanceShardUniqueAPI.class, CreateAliasAPI.class, diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateReplicaAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateReplica.java similarity index 66% rename from solr/core/src/java/org/apache/solr/handler/admin/api/CreateReplicaAPI.java rename to solr/core/src/java/org/apache/solr/handler/admin/api/CreateReplica.java index c119f27db60..2f34556dbda 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateReplicaAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateReplica.java @@ -17,7 +17,6 @@ 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; @@ -41,16 +40,12 @@ 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.client.api.endpoint.CreateReplicaApi; +import org.apache.solr.client.api.model.CreateReplicaRequestBody; import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse; import org.apache.solr.common.SolrException; import org.apache.solr.common.cloud.ZkNodeProps; @@ -59,35 +54,29 @@ 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.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; /** - * V2 API for adding a new replica to an existing shard. + * V2 API implementation 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 { +public class CreateReplica extends AdminAPIBase implements CreateReplicaApi { @Inject - public CreateReplicaAPI( + public CreateReplica( 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) + String collectionName, String shardName, CreateReplicaRequestBody requestBody) throws Exception { final var response = instantiateJerseyResponse(SubResponseAccumulatingJerseyResponse.class); if (requestBody == null) { @@ -103,12 +92,12 @@ public class CreateReplicaAPI extends AdminAPIBase { final ZkNodeProps remoteMessage = createRemoteMessage(resolvedCollectionName, shardName, requestBody); submitRemoteMessageAndHandleResponse( - response, CollectionParams.CollectionAction.ADDREPLICA, remoteMessage, requestBody.asyncId); + response, CollectionParams.CollectionAction.ADDREPLICA, remoteMessage, requestBody.async); return response; } public static ZkNodeProps createRemoteMessage( - String collectionName, String shardName, AddReplicaRequestBody requestBody) { + String collectionName, String shardName, CreateReplicaRequestBody requestBody) { final Map<String, Object> remoteMessage = new HashMap<>(); remoteMessage.put(QUEUE_OPERATION, CollectionParams.CollectionAction.ADDREPLICA.toLower()); remoteMessage.put(COLLECTION_PROP, collectionName); @@ -129,7 +118,7 @@ public class CreateReplicaAPI extends AdminAPIBase { insertIfNotNull(remoteMessage, TLOG_REPLICAS, requestBody.tlogReplicas); insertIfNotNull(remoteMessage, PULL_REPLICAS, requestBody.pullReplicas); insertIfNotNull(remoteMessage, FOLLOW_ALIASES, requestBody.followAliases); - insertIfNotNull(remoteMessage, ASYNC, requestBody.asyncId); + insertIfNotNull(remoteMessage, ASYNC, requestBody.async); if (requestBody.properties != null) { requestBody @@ -144,59 +133,31 @@ public class CreateReplicaAPI extends AdminAPIBase { return new ZkNodeProps(remoteMessage); } - public static class AddReplicaRequestBody implements JacksonReflectMapWriter { - @JsonProperty public String name; - @JsonProperty public String type; // TODO Make this an enum - see SOLR-15796 - @JsonProperty public String instanceDir; - @JsonProperty public String dataDir; - @JsonProperty public String ulogDir; - @JsonProperty public String route; - @JsonProperty public Integer nrtReplicas; - @JsonProperty public Integer tlogReplicas; - @JsonProperty public Integer pullReplicas; - @JsonProperty public Boolean waitForFinalState; - @JsonProperty public Boolean followAliases; - - @JsonProperty(ASYNC) - public String asyncId; - - // TODO This cluster of properties could probably be simplified down to just "nodeSet". See - // SOLR-15542 - @JsonProperty public String node; - - @JsonProperty("nodeSet") - public List<String> nodeSet; - - @JsonProperty public Boolean skipNodeAssignment; - - @JsonProperty public Map<String, String> properties; - - public static AddReplicaRequestBody fromV1Params(SolrParams params) { - final var requestBody = new AddReplicaRequestBody(); - - requestBody.name = params.get(NAME); - requestBody.type = params.get(REPLICA_TYPE); - requestBody.instanceDir = params.get(INSTANCE_DIR); - requestBody.dataDir = params.get(DATA_DIR); - requestBody.ulogDir = params.get(ULOG_DIR); - requestBody.route = params.get(_ROUTE_); - requestBody.nrtReplicas = params.getInt(NRT_REPLICAS); - requestBody.tlogReplicas = params.getInt(TLOG_REPLICAS); - requestBody.pullReplicas = params.getInt(PULL_REPLICAS); - requestBody.waitForFinalState = params.getBool(WAIT_FOR_FINAL_STATE); - requestBody.followAliases = params.getBool(FOLLOW_ALIASES); - requestBody.asyncId = params.get(ASYNC); - - requestBody.node = params.get(NODE); - if (params.get(CREATE_NODE_SET_PARAM) != null) { - requestBody.nodeSet = Arrays.asList(params.get(CREATE_NODE_SET).split(",")); - } - requestBody.skipNodeAssignment = params.getBool(SKIP_NODE_ASSIGNMENT); + public static CreateReplicaRequestBody createRequestBodyFromV1Params(SolrParams params) { + final var requestBody = new CreateReplicaRequestBody(); + + requestBody.name = params.get(NAME); + requestBody.type = params.get(REPLICA_TYPE); + requestBody.instanceDir = params.get(INSTANCE_DIR); + requestBody.dataDir = params.get(DATA_DIR); + requestBody.ulogDir = params.get(ULOG_DIR); + requestBody.route = params.get(_ROUTE_); + requestBody.nrtReplicas = params.getInt(NRT_REPLICAS); + requestBody.tlogReplicas = params.getInt(TLOG_REPLICAS); + requestBody.pullReplicas = params.getInt(PULL_REPLICAS); + requestBody.waitForFinalState = params.getBool(WAIT_FOR_FINAL_STATE); + requestBody.followAliases = params.getBool(FOLLOW_ALIASES); + requestBody.async = params.get(ASYNC); + + requestBody.node = params.get(NODE); + if (params.get(CREATE_NODE_SET_PARAM) != null) { + requestBody.nodeSet = Arrays.asList(params.get(CREATE_NODE_SET).split(",")); + } + requestBody.skipNodeAssignment = params.getBool(SKIP_NODE_ASSIGNMENT); - requestBody.properties = - copyPrefixedPropertiesWithoutPrefix(params, new HashMap<>(), PROPERTY_PREFIX); + requestBody.properties = + copyPrefixedPropertiesWithoutPrefix(params, new HashMap<>(), PROPERTY_PREFIX); - return requestBody; - } + return requestBody; } } diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/CreateReplicaAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/CreateReplicaAPITest.java index d4e9772e866..2abe5d13397 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/CreateReplicaAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/CreateReplicaAPITest.java @@ -39,11 +39,12 @@ 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.client.api.model.CreateReplicaRequestBody; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.ModifiableSolrParams; import org.junit.Test; -/** Unit tests for {@link CreateReplicaAPI} */ +/** Unit tests for {@link CreateReplica} */ public class CreateReplicaAPITest extends SolrTestCaseJ4 { @Test public void testReportsErrorIfRequestBodyMissing() { @@ -51,7 +52,7 @@ public class CreateReplicaAPITest extends SolrTestCaseJ4 { expectThrows( SolrException.class, () -> { - final var api = new CreateReplicaAPI(null, null, null); + final var api = new CreateReplica(null, null, null); api.createReplica("someCollName", "someShardName", null); }); @@ -61,12 +62,12 @@ public class CreateReplicaAPITest extends SolrTestCaseJ4 { @Test public void testReportsErrorIfCollectionNameMissing() { - final var requestBody = new CreateReplicaAPI.AddReplicaRequestBody(); + final var requestBody = new CreateReplicaRequestBody(); final SolrException thrown = expectThrows( SolrException.class, () -> { - final var api = new CreateReplicaAPI(null, null, null); + final var api = new CreateReplica(null, null, null); api.createReplica(null, "shardName", requestBody); }); @@ -76,12 +77,12 @@ public class CreateReplicaAPITest extends SolrTestCaseJ4 { @Test public void testReportsErrorIfShardNameMissing() { - final var requestBody = new CreateReplicaAPI.AddReplicaRequestBody(); + final var requestBody = new CreateReplicaRequestBody(); final SolrException thrown = expectThrows( SolrException.class, () -> { - final var api = new CreateReplicaAPI(null, null, null); + final var api = new CreateReplica(null, null, null); api.createReplica("someCollectionName", null, requestBody); }); @@ -91,7 +92,7 @@ public class CreateReplicaAPITest extends SolrTestCaseJ4 { @Test public void testCreateRemoteMessageAllProperties() { - final var requestBody = new CreateReplicaAPI.AddReplicaRequestBody(); + final var requestBody = new CreateReplicaRequestBody(); requestBody.name = "someName"; requestBody.type = "NRT"; requestBody.instanceDir = "/some/dir1"; @@ -106,11 +107,11 @@ public class CreateReplicaAPITest extends SolrTestCaseJ4 { requestBody.skipNodeAssignment = Boolean.TRUE; requestBody.waitForFinalState = true; requestBody.followAliases = true; - requestBody.asyncId = "someAsyncId"; + requestBody.async = "someAsyncId"; requestBody.properties = Map.of("propName1", "propVal1", "propName2", "propVal2"); final var remoteMessage = - CreateReplicaAPI.createRemoteMessage("someCollectionName", "someShardName", requestBody) + CreateReplica.createRemoteMessage("someCollectionName", "someShardName", requestBody) .getProperties(); assertEquals(20, remoteMessage.size()); @@ -159,7 +160,7 @@ public class CreateReplicaAPITest extends SolrTestCaseJ4 { v1Params.add("property.propName1", "propVal1"); v1Params.add("property.propName2", "propVal2"); - final var requestBody = CreateReplicaAPI.AddReplicaRequestBody.fromV1Params(v1Params); + final var requestBody = CreateReplica.createRequestBodyFromV1Params(v1Params); assertEquals("someName", requestBody.name); assertEquals("NRT", requestBody.type); @@ -175,7 +176,7 @@ public class CreateReplicaAPITest extends SolrTestCaseJ4 { assertEquals(Boolean.TRUE, requestBody.skipNodeAssignment); assertEquals(Boolean.TRUE, requestBody.waitForFinalState); assertEquals(Boolean.TRUE, requestBody.followAliases); - assertEquals("someAsyncId", requestBody.asyncId); + assertEquals("someAsyncId", requestBody.async); assertEquals("propVal1", requestBody.properties.get("propName1")); assertEquals("propVal2", requestBody.properties.get("propName2")); } diff --git a/solr/core/src/test/org/apache/solr/jersey/PostRequestLoggingFilterTest.java b/solr/core/src/test/org/apache/solr/jersey/PostRequestLoggingFilterTest.java index c0770c4912e..985085b7829 100644 --- a/solr/core/src/test/org/apache/solr/jersey/PostRequestLoggingFilterTest.java +++ b/solr/core/src/test/org/apache/solr/jersey/PostRequestLoggingFilterTest.java @@ -26,7 +26,7 @@ import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.UriInfo; import org.apache.solr.SolrTestCaseJ4; -import org.apache.solr.handler.admin.api.CreateReplicaAPI; +import org.apache.solr.client.api.model.CreateReplicaRequestBody; import org.junit.BeforeClass; import org.junit.Test; @@ -122,10 +122,10 @@ public class PostRequestLoggingFilterTest extends SolrTestCaseJ4 { @Test public void testRequestBodyRepresentedAsJsonWhenFound() { - final var requestBody = new CreateReplicaAPI.AddReplicaRequestBody(); + final var requestBody = new CreateReplicaRequestBody(); requestBody.name = "someReplicaName"; requestBody.type = "NRT"; - requestBody.asyncId = "someAsyncId"; + requestBody.async = "someAsyncId"; final var mockContext = mock(ContainerRequestContext.class); when(mockContext.getProperty(DESERIALIZED_REQUEST_BODY_KEY)).thenReturn(requestBody);
