This is an automated email from the ASF dual-hosted git repository. gerlowskija pushed a commit to branch branch_9x in repository https://gitbox.apache.org/repos/asf/solr.git
commit d06d5faa1f815acae7281550c1fa6e5f856c480e Author: Jason Gerlowski <[email protected]> AuthorDate: Tue Oct 3 14:38:47 2023 -0400 SOLR-16825: Migrate v2 definitions to 'api' module, pt 4 (#1960) This commit covers the create APIs for collections, shards and replicas. It also covers the collection and core level "install-shard-data" APIs. Extracting annotated interfaces for these APIs includes them in the SolrRequest- generation we now do in SolrJ --- .../client/api/endpoint/CreateCollectionApi.java | 34 +++ .../solr/client/api/endpoint/CreateReplicaApi.java | 44 +++ .../solr/client/api/endpoint/CreateShardApi.java | 37 +++ .../client/api/endpoint/InstallCoreDataApi.java | 42 +++ .../client/api/endpoint/InstallShardDataApi.java | 38 +++ .../api/model/CreateCollectionRequestBody.java | 63 +++++ .../model/CreateCollectionRouterProperties.java | 25 ++ .../client/api/model/CreateReplicaRequestBody.java | 48 ++++ .../client/api/model/CreateShardRequestBody.java | 50 ++++ .../api/model/InstallCoreDataRequestBody.java | 30 ++ .../api/model/InstallShardDataRequestBody.java | 28 ++ .../solr/cloud/api/collections/AliasCmd.java | 9 +- .../solr/handler/admin/CollectionsHandler.java | 37 ++- .../solr/handler/admin/CoreAdminHandler.java | 4 +- .../solr/handler/admin/InstallCoreDataOp.java | 12 +- .../solr/handler/admin/api/CreateAliasAPI.java | 12 +- ...ateCollectionAPI.java => CreateCollection.java} | 303 ++++++++------------- .../admin/api/CreateCollectionBackupAPI.java | 2 +- .../{CreateReplicaAPI.java => CreateReplica.java} | 108 +++----- .../api/{CreateShardAPI.java => CreateShard.java} | 113 +++----- ...nstallCoreDataAPI.java => InstallCoreData.java} | 41 +-- ...tallShardDataAPI.java => InstallShardData.java} | 45 +-- .../handler/admin/api/RestoreCollectionAPI.java | 11 +- .../solr/jersey/PostRequestLoggingFilter.java | 27 +- .../solr/handler/admin/api/CreateAliasAPITest.java | 15 +- .../handler/admin/api/CreateCollectionAPITest.java | 34 +-- .../handler/admin/api/CreateReplicaAPITest.java | 23 +- .../solr/handler/admin/api/CreateShardAPITest.java | 27 +- .../admin/api/RestoreCollectionAPITest.java | 3 +- .../solr/jersey/PostRequestLoggingFilterTest.java | 6 +- .../org/apache/solr/gcs/GCSInstallShardTest.java | 3 +- .../org/apache/solr/s3/S3InstallShardTest.java | 3 +- .../solrj/src/resources/java-template/api.mustache | 19 +- .../api/collections/AbstractInstallShardTest.java | 3 +- 34 files changed, 779 insertions(+), 520 deletions(-) diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/CreateCollectionApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/CreateCollectionApi.java new file mode 100644 index 00000000000..748b27972e6 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/CreateCollectionApi.java @@ -0,0 +1,34 @@ +/* + * 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 org.apache.solr.client.api.model.CreateCollectionRequestBody; +import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse; + +/** V2 API definition for creating a SolrCloud collection */ +@Path("/collections") +public interface CreateCollectionApi { + @POST + @Operation( + summary = "Creates a new SolrCloud collection.", + tags = {"collections"}) + SubResponseAccumulatingJerseyResponse createCollection(CreateCollectionRequestBody requestBody) + throws Exception; +} 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/endpoint/CreateShardApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/CreateShardApi.java new file mode 100644 index 00000000000..0bd30763811 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/CreateShardApi.java @@ -0,0 +1,37 @@ +/* + * 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.CreateShardRequestBody; +import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse; + +/** V2 API definition for creating a new shard in a collection. */ +@Path("/collections/{collectionName}/shards") +public interface CreateShardApi { + + @POST + @Operation( + summary = "Create a new shard in an existing collection", + tags = {"shards"}) + SubResponseAccumulatingJerseyResponse createShard( + @PathParam("collectionName") String collectionName, CreateShardRequestBody requestBody) + throws Exception; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/InstallCoreDataApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/InstallCoreDataApi.java new file mode 100644 index 00000000000..4ee95539571 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/InstallCoreDataApi.java @@ -0,0 +1,42 @@ +/* + * 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.InstallCoreDataRequestBody; +import org.apache.solr.client.api.model.SolrJerseyResponse; + +/** + * V2 API definition for installing an offline index to a single core of a shard. + * + * <p>This is an internal API intended for use only by the Collection Admin "Install Shard Data" + * API. + */ +@Path("/cores/{coreName}/install") +public interface InstallCoreDataApi { + + @POST + @Operation( + summary = "Install an offline index to a specified core", + tags = {"cores"}) + SolrJerseyResponse installCoreData( + @PathParam("coreName") String coreName, InstallCoreDataRequestBody requestBody) + throws Exception; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/InstallShardDataApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/InstallShardDataApi.java new file mode 100644 index 00000000000..65648ad9117 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/InstallShardDataApi.java @@ -0,0 +1,38 @@ +/* + * 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.InstallShardDataRequestBody; +import org.apache.solr.client.api.model.SolrJerseyResponse; + +/** V2 API definition allowing users to import offline-constructed index into a shard. */ +@Path("/collections/{collName}/shards/{shardName}/install") +public interface InstallShardDataApi { + @POST + @Operation( + summary = "Install offline index into an existing shard", + tags = {"shards"}) + SolrJerseyResponse installShardData( + @PathParam("collName") String collName, + @PathParam("shardName") String shardName, + InstallShardDataRequestBody requestBody) + throws Exception; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CreateCollectionRequestBody.java b/solr/api/src/java/org/apache/solr/client/api/model/CreateCollectionRequestBody.java new file mode 100644 index 00000000000..5881708ee34 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/CreateCollectionRequestBody.java @@ -0,0 +1,63 @@ +/* + * 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; + +/** Request body for v2 "create collection" requests */ +public class CreateCollectionRequestBody { + @JsonProperty public String name; + + @JsonProperty public Integer replicationFactor; + + @JsonProperty public String config; + + @JsonProperty public Integer numShards; + + @JsonProperty public List<String> shardNames; + + @JsonProperty public Integer pullReplicas; + + @JsonProperty public Integer tlogReplicas; + + @JsonProperty public Integer nrtReplicas; + + @JsonProperty public Boolean waitForFinalState; + + @JsonProperty public Boolean perReplicaState; + + @JsonProperty public String alias; + + @JsonProperty public Map<String, String> properties; + + @JsonProperty public String async; + + @JsonProperty public CreateCollectionRouterProperties router; + + // Parameters below differ from v1 API + // V1 API uses createNodeSet + @JsonProperty("nodeSet") + public List<String> nodeSet; + // v1 API uses createNodeSet=EMPTY + @JsonProperty("createReplicas") + public Boolean createReplicas; + // V1 API uses 'createNodeSet.shuffle' + @JsonProperty("shuffleNodes") + public Boolean shuffleNodes; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CreateCollectionRouterProperties.java b/solr/api/src/java/org/apache/solr/client/api/model/CreateCollectionRouterProperties.java new file mode 100644 index 00000000000..382091ee60c --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/CreateCollectionRouterProperties.java @@ -0,0 +1,25 @@ +/* + * 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; + +public class CreateCollectionRouterProperties { + @JsonProperty public String name; + + @JsonProperty public String field; +} 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/api/src/java/org/apache/solr/client/api/model/CreateShardRequestBody.java b/solr/api/src/java/org/apache/solr/client/api/model/CreateShardRequestBody.java new file mode 100644 index 00000000000..ea871998cca --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/CreateShardRequestBody.java @@ -0,0 +1,50 @@ +/* + * 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 io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import java.util.Map; + +public class CreateShardRequestBody { + @Schema(name = "shardName") + @JsonProperty("name") + public String shardName; + + @JsonProperty public Integer replicationFactor; + + @JsonProperty public Integer nrtReplicas; + + @JsonProperty public Integer tlogReplicas; + + @JsonProperty public Integer pullReplicas; + + @JsonProperty("createReplicas") + public Boolean createReplicas; + + @JsonProperty("nodeSet") + public List<String> nodeSet; + + @JsonProperty public Boolean waitForFinalState; + + @JsonProperty public Boolean followAliases; + + @JsonProperty public String async; + + @JsonProperty public Map<String, String> properties; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/InstallCoreDataRequestBody.java b/solr/api/src/java/org/apache/solr/client/api/model/InstallCoreDataRequestBody.java new file mode 100644 index 00000000000..2ba9d09c1f6 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/InstallCoreDataRequestBody.java @@ -0,0 +1,30 @@ +/* + * 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; + +public class InstallCoreDataRequestBody { + // Expected to point to an index directory (e.g. data/techproducts_shard1_replica_n1/data/index) + // for a single core that has previously been uploaded to the backup repository previously + // uploaded to the backup repository. + @JsonProperty public String location; + + @JsonProperty public String repository; + + @JsonProperty public String asyncId; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/InstallShardDataRequestBody.java b/solr/api/src/java/org/apache/solr/client/api/model/InstallShardDataRequestBody.java new file mode 100644 index 00000000000..31bec8eb434 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/InstallShardDataRequestBody.java @@ -0,0 +1,28 @@ +/* + * 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; + +public class InstallShardDataRequestBody { + @JsonProperty(value = "location", required = true) + public String location; + + @JsonProperty public String repository; + + @JsonProperty public String async; +} diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/AliasCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/AliasCmd.java index 92b18b53a74..4815a76b807 100644 --- a/solr/core/src/java/org/apache/solr/cloud/api/collections/AliasCmd.java +++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/AliasCmd.java @@ -31,7 +31,7 @@ import org.apache.solr.common.cloud.ZkNodeProps; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.handler.admin.CollectionsHandler; -import org.apache.solr.handler.admin.api.CreateCollectionAPI; +import org.apache.solr.handler.admin.api.CreateCollection; /** * Common superclass for commands that maintain or manipulate aliases. In the routed alias parlance, @@ -73,10 +73,9 @@ abstract class AliasCmd implements CollApiCmds.CollectionApiCommand { // a CollectionOperation reads params and produces a message (Map) that is supposed to be sent // to the Overseer. Although we could create the Map without it, there are a fair amount of // rules we don't want to reproduce. - final var createReqBody = - CreateCollectionAPI.CreateCollectionRequestBody.fromV1Params(createReqParams, true); - CreateCollectionAPI.populateDefaultsIfNecessary(ccc.getCoreContainer(), createReqBody); - final ZkNodeProps createMessage = CreateCollectionAPI.createRemoteMessage(createReqBody); + final var createReqBody = CreateCollection.createRequestBodyFromV1Params(createReqParams, true); + CreateCollection.populateDefaultsIfNecessary(ccc.getCoreContainer(), createReqBody); + final ZkNodeProps createMessage = CreateCollection.createRemoteMessage(createReqBody); NamedList<Object> results = new NamedList<>(); try { 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 97de7a36bb3..d91f4e1914b 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 @@ -123,6 +123,7 @@ import org.apache.solr.api.AnnotatedApi; import org.apache.solr.api.Api; import org.apache.solr.api.JerseyResource; import org.apache.solr.client.api.model.AddReplicaPropertyRequestBody; +import org.apache.solr.client.api.model.InstallShardDataRequestBody; import org.apache.solr.client.api.model.ReplaceNodeRequestBody; import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.client.api.model.UpdateAliasPropertiesRequestBody; @@ -172,11 +173,11 @@ import org.apache.solr.handler.admin.api.BalanceShardUniqueAPI; import org.apache.solr.handler.admin.api.CollectionPropertyAPI; import org.apache.solr.handler.admin.api.CollectionStatusAPI; import org.apache.solr.handler.admin.api.CreateAliasAPI; -import org.apache.solr.handler.admin.api.CreateCollectionAPI; +import org.apache.solr.handler.admin.api.CreateCollection; 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.CreateShardAPI; +import org.apache.solr.handler.admin.api.CreateReplica; +import org.apache.solr.handler.admin.api.CreateShard; import org.apache.solr.handler.admin.api.DeleteAlias; import org.apache.solr.handler.admin.api.DeleteCollection; import org.apache.solr.handler.admin.api.DeleteCollectionBackup; @@ -186,7 +187,7 @@ import org.apache.solr.handler.admin.api.DeleteReplica; import org.apache.solr.handler.admin.api.DeleteReplicaProperty; import org.apache.solr.handler.admin.api.DeleteShardAPI; import org.apache.solr.handler.admin.api.ForceLeader; -import org.apache.solr.handler.admin.api.InstallShardDataAPI; +import org.apache.solr.handler.admin.api.InstallShardData; import org.apache.solr.handler.admin.api.ListAliases; import org.apache.solr.handler.admin.api.ListCollectionBackups; import org.apache.solr.handler.admin.api.ListCollectionSnapshotsAPI; @@ -507,9 +508,9 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission CREATE_OP( CREATE, (req, rsp, h) -> { - final CreateCollectionAPI.CreateCollectionRequestBody requestBody = - CreateCollectionAPI.CreateCollectionRequestBody.fromV1Params(req.getParams(), true); - final CreateCollectionAPI createApi = new CreateCollectionAPI(h.coreContainer, req, rsp); + final var requestBody = + CreateCollection.createRequestBodyFromV1Params(req.getParams(), true); + final CreateCollection createApi = new CreateCollection(h.coreContainer, req, rsp); final SolrJerseyResponse response = createApi.createCollection(requestBody); // 'rsp' may be null, as when overseer commands execute CollectionAction impl's directly. @@ -729,7 +730,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission CREATESHARD_OP( CREATESHARD, (req, rsp, h) -> { - CreateShardAPI.invokeFromV1Params(h.coreContainer, req, rsp); + CreateShard.invokeFromV1Params(h.coreContainer, req, rsp); return null; }), DELETEREPLICA_OP( @@ -942,9 +943,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); @@ -1081,13 +1081,12 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission req.getParams().required().check(COLLECTION, SHARD); final String collectionName = req.getParams().get(COLLECTION); final String shardName = req.getParams().get(SHARD); - final InstallShardDataAPI.InstallShardRequestBody reqBody = - new InstallShardDataAPI.InstallShardRequestBody(); - reqBody.asyncId = req.getParams().get(ASYNC); + final InstallShardDataRequestBody reqBody = new InstallShardDataRequestBody(); + reqBody.async = req.getParams().get(ASYNC); reqBody.repository = req.getParams().get(BACKUP_REPOSITORY); reqBody.location = req.getParams().get(BACKUP_LOCATION); - final InstallShardDataAPI installApi = new InstallShardDataAPI(h.coreContainer, req, rsp); + final InstallShardData installApi = new InstallShardData(h.coreContainer, req, rsp); final SolrJerseyResponse installResponse = installApi.installShardData(collectionName, shardName, reqBody); V2ApiUtils.squashIntoSolrResponseWithoutHeader(rsp, installResponse); @@ -1362,13 +1361,13 @@ 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, - CreateCollectionAPI.class, + CreateCollection.class, CreateCollectionBackupAPI.class, - CreateShardAPI.class, + CreateShard.class, DeleteAlias.class, DeleteCollectionBackup.class, DeleteCollection.class, @@ -1376,7 +1375,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission DeleteReplicaProperty.class, DeleteShardAPI.class, ForceLeader.class, - InstallShardDataAPI.class, + InstallShardData.class, ListCollections.class, ListCollectionBackups.class, ReloadCollectionAPI.class, diff --git a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java index d928282df5c..d9ece272f75 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java @@ -58,7 +58,7 @@ import org.apache.solr.handler.admin.api.AllCoresStatusAPI; import org.apache.solr.handler.admin.api.BackupCoreAPI; import org.apache.solr.handler.admin.api.CoreSnapshotAPI; import org.apache.solr.handler.admin.api.CreateCoreAPI; -import org.apache.solr.handler.admin.api.InstallCoreDataAPI; +import org.apache.solr.handler.admin.api.InstallCoreData; import org.apache.solr.handler.admin.api.MergeIndexesAPI; import org.apache.solr.handler.admin.api.OverseerOperationAPI; import org.apache.solr.handler.admin.api.PrepareCoreRecoveryAPI; @@ -403,7 +403,7 @@ public class CoreAdminHandler extends RequestHandlerBase implements PermissionNa public Collection<Class<? extends JerseyResource>> getJerseyResources() { return List.of( CoreSnapshotAPI.class, - InstallCoreDataAPI.class, + InstallCoreData.class, BackupCoreAPI.class, RestoreCoreAPI.class, ReloadCoreAPI.class); diff --git a/solr/core/src/java/org/apache/solr/handler/admin/InstallCoreDataOp.java b/solr/core/src/java/org/apache/solr/handler/admin/InstallCoreDataOp.java index c115739f6a8..7d7ada266a3 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/InstallCoreDataOp.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/InstallCoreDataOp.java @@ -21,9 +21,10 @@ import static org.apache.solr.common.params.CommonAdminParams.ASYNC; import static org.apache.solr.common.params.CoreAdminParams.BACKUP_LOCATION; import static org.apache.solr.common.params.CoreAdminParams.BACKUP_REPOSITORY; +import org.apache.solr.client.api.model.InstallCoreDataRequestBody; import org.apache.solr.common.params.CoreAdminParams; import org.apache.solr.common.params.SolrParams; -import org.apache.solr.handler.admin.api.InstallCoreDataAPI; +import org.apache.solr.handler.admin.api.InstallCoreData; import org.apache.solr.handler.api.V2ApiUtils; /** @@ -31,7 +32,7 @@ import org.apache.solr.handler.api.V2ApiUtils; * "Install Shard Data" Collection-Admin functionality * * <p>Converts v1-style query parameters into a v2-style request body and delegating to {@link - * InstallCoreDataAPI}. + * InstallCoreData}. */ public class InstallCoreDataOp implements CoreAdminHandler.CoreAdminOp { @Override @@ -39,11 +40,10 @@ public class InstallCoreDataOp implements CoreAdminHandler.CoreAdminOp { final SolrParams params = it.req.getParams(); final String coreName = params.required().get(CoreAdminParams.CORE); - final InstallCoreDataAPI api = - new InstallCoreDataAPI( + final InstallCoreData api = + new InstallCoreData( it.handler.getCoreContainer(), it.handler.getCoreAdminAsyncTracker(), it.req, it.rsp); - final InstallCoreDataAPI.InstallCoreDataRequestBody requestBody = - new InstallCoreDataAPI.InstallCoreDataRequestBody(); + final InstallCoreDataRequestBody requestBody = new InstallCoreDataRequestBody(); requestBody.repository = params.get(BACKUP_REPOSITORY); requestBody.location = params.get(BACKUP_LOCATION); requestBody.asyncId = params.get(ASYNC); diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateAliasAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateAliasAPI.java index ad3f300b7c9..dfc09e917c1 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateAliasAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateAliasAPI.java @@ -47,6 +47,7 @@ import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; +import org.apache.solr.client.api.model.CreateCollectionRequestBody; import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse; import org.apache.solr.client.solrj.RoutedAliasTypes; @@ -159,8 +160,8 @@ public class CreateAliasAPI extends AdminAPIBase { } if (requestBody.collCreationParameters != null) { - requestBody.collCreationParameters.addToRemoteMessageWithPrefix( - remoteMessage, "create-collection."); + CreateCollection.addToRemoteMessageWithPrefix( + requestBody.collCreationParameters, remoteMessage, "create-collection."); } return new ZkNodeProps(remoteMessage); } @@ -203,7 +204,7 @@ public class CreateAliasAPI extends AdminAPIBase { final SolrParams createCollectionParams = getHierarchicalParametersByPrefix(params, CREATE_COLLECTION_PREFIX); createBody.collCreationParameters = - CreateCollectionAPI.CreateCollectionRequestBody.fromV1Params(createCollectionParams, false); + CreateCollection.createRequestBodyFromV1Params(createCollectionParams, false); return createBody; } @@ -239,7 +240,7 @@ public class CreateAliasAPI extends AdminAPIBase { public List<RoutedAliasProperties> routers; @JsonProperty("create-collection") - public CreateCollectionAPI.CreateCollectionRequestBody collCreationParameters; + public CreateCollectionRequestBody collCreationParameters; public void validate() { SolrIdentifierValidator.validateAliasName(name); @@ -257,8 +258,7 @@ public class CreateAliasAPI extends AdminAPIBase { BAD_REQUEST, "Collections cannot be specified when creating a routed alias."); } - final CreateCollectionAPI.CreateCollectionRequestBody createCollReqBody = - collCreationParameters; + final var createCollReqBody = collCreationParameters; if (createCollReqBody != null) { if (createCollReqBody.name != null) { throw new SolrException( diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollection.java similarity index 62% rename from solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionAPI.java rename to solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollection.java index b8ae2dca46f..8ce1eca623e 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollection.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.client.solrj.request.beans.V2ApiConstants.ROUTER_KEY; import static org.apache.solr.client.solrj.request.beans.V2ApiConstants.SHARD_NAMES; import static org.apache.solr.cloud.Overseer.QUEUE_OPERATION; @@ -36,15 +35,12 @@ import static org.apache.solr.common.params.CollectionAdminParams.REPLICATION_FA import static org.apache.solr.common.params.CollectionAdminParams.TLOG_REPLICAS; 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.CONFIG; import static org.apache.solr.common.params.CoreAdminParams.NAME; import static org.apache.solr.handler.admin.CollectionsHandler.DEFAULT_COLLECTION_OP_TIMEOUT; import static org.apache.solr.handler.admin.CollectionsHandler.waitForActiveCollection; import static org.apache.solr.handler.api.V2ApiUtils.flattenMapWithPrefix; -import static org.apache.solr.schema.IndexSchema.FIELD; import static org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM; -import com.fasterxml.jackson.annotation.JsonProperty; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -57,10 +53,9 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import javax.inject.Inject; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import org.apache.solr.client.api.endpoint.CreateCollectionApi; +import org.apache.solr.client.api.model.CreateCollectionRequestBody; +import org.apache.solr.client.api.model.CreateCollectionRouterProperties; import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse; import org.apache.solr.client.solrj.SolrResponse; import org.apache.solr.client.solrj.request.beans.V2ApiConstants; @@ -76,9 +71,9 @@ import org.apache.solr.common.params.CollectionParams; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.CollectionUtil; +import org.apache.solr.common.util.Utils; import org.apache.solr.core.CoreContainer; import org.apache.solr.handler.admin.CollectionsHandler; -import org.apache.solr.jersey.JacksonReflectMapWriter; import org.apache.solr.jersey.PermissionName; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; @@ -90,19 +85,17 @@ import org.apache.zookeeper.KeeperException; * * <p>This API is analogous to the v1 /admin/collections?action=CREATE command. */ -@Path("/collections") -public class CreateCollectionAPI extends AdminAPIBase { +public class CreateCollection extends AdminAPIBase implements CreateCollectionApi { @Inject - public CreateCollectionAPI( + public CreateCollection( CoreContainer coreContainer, SolrQueryRequest solrQueryRequest, SolrQueryResponse solrQueryResponse) { super(coreContainer, solrQueryRequest, solrQueryResponse); } - @POST - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2}) + @Override @PermissionName(COLL_EDIT_PERM) public SubResponseAccumulatingJerseyResponse createCollection( CreateCollectionRequestBody requestBody) throws Exception { @@ -123,7 +116,7 @@ public class CreateCollectionAPI extends AdminAPIBase { createSysConfigSet(coreContainer); } - requestBody.validate(); + validateRequestBody(requestBody); // Populate any 'null' creation parameters that support COLLECTIONPROP defaults. populateDefaultsIfNecessary(coreContainer, requestBody); @@ -224,7 +217,7 @@ public class CreateCollectionAPI extends AdminAPIBase { } if (reqBody.router != null) { - final RouterProperties routerProps = reqBody.router; + final var routerProps = reqBody.router; rawProperties.put("router.name", routerProps.name); rawProperties.put("router.field", routerProps.field); } @@ -297,185 +290,127 @@ public class CreateCollectionAPI extends AdminAPIBase { } } - /** Request body for v2 "create collection" requests */ - public static class CreateCollectionRequestBody implements JacksonReflectMapWriter { - @JsonProperty(NAME) - public String name; - - @JsonProperty(REPLICATION_FACTOR) - public Integer replicationFactor; - - @JsonProperty(CONFIG) - public String config; - - @JsonProperty(NUM_SLICES) - public Integer numShards; - - @JsonProperty(SHARD_NAMES) - public List<String> shardNames; - - @JsonProperty(PULL_REPLICAS) - public Integer pullReplicas; - - @JsonProperty(TLOG_REPLICAS) - public Integer tlogReplicas; - - @JsonProperty(NRT_REPLICAS) - public Integer nrtReplicas; - - @JsonProperty(WAIT_FOR_FINAL_STATE) - public Boolean waitForFinalState; - - @JsonProperty(PER_REPLICA_STATE) - public Boolean perReplicaState; - - @JsonProperty(ALIAS) - public String alias; - - @JsonProperty("properties") - public Map<String, String> properties; - - @JsonProperty(ASYNC) - public String async; - - @JsonProperty("router") - public RouterProperties router; - - // Parameters below differ from v1 API - // V1 API uses createNodeSet - @JsonProperty("nodeSet") - public List<String> nodeSet; - // v1 API uses createNodeSet=EMPTY - @JsonProperty("createReplicas") - public Boolean createReplicas; - // V1 API uses 'createNodeSet.shuffle' - @JsonProperty("shuffleNodes") - public Boolean shuffleNodes; - - public static CreateCollectionRequestBody fromV1Params( - SolrParams params, boolean nameRequired) { - final var requestBody = new CreateCollectionRequestBody(); - requestBody.name = - nameRequired ? params.required().get(CommonParams.NAME) : params.get(CommonParams.NAME); - requestBody.replicationFactor = params.getInt(ZkStateReader.REPLICATION_FACTOR); - requestBody.config = params.get(COLL_CONF); - requestBody.numShards = params.getInt(NUM_SLICES); - if (params.get(CREATE_NODE_SET) != null) { - final String commaDelimNodeSet = params.get(CREATE_NODE_SET); - if ("EMPTY".equals(commaDelimNodeSet)) { - requestBody.createReplicas = false; - } else { - requestBody.nodeSet = Arrays.asList(params.get(CREATE_NODE_SET).split(",")); - } - } - requestBody.shuffleNodes = params.getBool(CREATE_NODE_SET_SHUFFLE); - requestBody.shardNames = - params.get(SHARDS_PROP) != null - ? Arrays.stream(params.get(SHARDS_PROP).split(",")).collect(Collectors.toList()) - : new ArrayList<>(); - requestBody.tlogReplicas = params.getInt(ZkStateReader.TLOG_REPLICAS); - requestBody.pullReplicas = params.getInt(ZkStateReader.PULL_REPLICAS); - requestBody.nrtReplicas = params.getInt(ZkStateReader.NRT_REPLICAS); - requestBody.waitForFinalState = params.getBool(WAIT_FOR_FINAL_STATE); - requestBody.perReplicaState = params.getBool(PER_REPLICA_STATE); - requestBody.alias = params.get(ALIAS); - requestBody.async = params.get(ASYNC); - requestBody.properties = - copyPrefixedPropertiesWithoutPrefix(params, new HashMap<>(), PROPERTY_PREFIX); - if (params.get("router.name") != null || params.get("router.field") != null) { - final RouterProperties routerProperties = new RouterProperties(); - routerProperties.name = params.get("router.name"); - routerProperties.field = params.get("router.field"); - requestBody.router = routerProperties; + public static CreateCollectionRequestBody createRequestBodyFromV1Params( + SolrParams params, boolean nameRequired) { + final var requestBody = new CreateCollectionRequestBody(); + requestBody.name = + nameRequired ? params.required().get(CommonParams.NAME) : params.get(CommonParams.NAME); + requestBody.replicationFactor = params.getInt(ZkStateReader.REPLICATION_FACTOR); + requestBody.config = params.get(COLL_CONF); + requestBody.numShards = params.getInt(NUM_SLICES); + if (params.get(CREATE_NODE_SET) != null) { + final String commaDelimNodeSet = params.get(CREATE_NODE_SET); + if ("EMPTY".equals(commaDelimNodeSet)) { + requestBody.createReplicas = false; + } else { + requestBody.nodeSet = Arrays.asList(params.get(CREATE_NODE_SET).split(",")); } - - return requestBody; + } + requestBody.shuffleNodes = params.getBool(CREATE_NODE_SET_SHUFFLE); + requestBody.shardNames = + params.get(SHARDS_PROP) != null + ? Arrays.stream(params.get(SHARDS_PROP).split(",")).collect(Collectors.toList()) + : new ArrayList<>(); + requestBody.tlogReplicas = params.getInt(ZkStateReader.TLOG_REPLICAS); + requestBody.pullReplicas = params.getInt(ZkStateReader.PULL_REPLICAS); + requestBody.nrtReplicas = params.getInt(ZkStateReader.NRT_REPLICAS); + requestBody.waitForFinalState = params.getBool(WAIT_FOR_FINAL_STATE); + requestBody.perReplicaState = params.getBool(PER_REPLICA_STATE); + requestBody.alias = params.get(ALIAS); + requestBody.async = params.get(ASYNC); + requestBody.properties = + copyPrefixedPropertiesWithoutPrefix(params, new HashMap<>(), PROPERTY_PREFIX); + if (params.get("router.name") != null || params.get("router.field") != null) { + final var routerProperties = new CreateCollectionRouterProperties(); + routerProperties.name = params.get("router.name"); + routerProperties.field = params.get("router.field"); + requestBody.router = routerProperties; } - public void validate() { - if (replicationFactor != null - && nrtReplicas != null - && (!replicationFactor.equals(nrtReplicas))) { - throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, - "Cannot specify both replicationFactor and nrtReplicas as they mean the same thing"); - } - - SolrIdentifierValidator.validateCollectionName(name); + return requestBody; + } - if (shardNames != null && !shardNames.isEmpty()) { - verifyShardsParam(shardNames); - } + public static void validateRequestBody(CreateCollectionRequestBody requestBody) { + if (requestBody.replicationFactor != null + && requestBody.nrtReplicas != null + && (!requestBody.replicationFactor.equals(requestBody.nrtReplicas))) { + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, + "Cannot specify both replicationFactor and nrtReplicas as they mean the same thing"); } - public void addToRemoteMessageWithPrefix(Map<String, Object> remoteMessage, String prefix) { - final Map<String, Object> v1Params = toMap(new HashMap<>()); - convertV2CreateCollectionMapToV1ParamMap(v1Params); - for (Map.Entry<String, Object> v1Param : v1Params.entrySet()) { - remoteMessage.put(prefix + v1Param.getKey(), v1Param.getValue()); - } + SolrIdentifierValidator.validateCollectionName(requestBody.name); + + if (requestBody.shardNames != null && !requestBody.shardNames.isEmpty()) { + verifyShardsParam(requestBody.shardNames); } + } - /** - * Convert a map representing the v2 request body into v1-appropriate query-parameters. - * - * <p>Most v2 APIs using the legacy (i.e. non-JAX-RS) framework implement the v2 API by - * restructuring the provided parameters so that the v1 codepath can be called. This utility - * method is provided in pursuit of that usecase. It's not used directly CreateCollectionAPI, - * which uses the JAX-RS framework, but it's kept here so that logic surrounding - * collection-creation parameters can be kept in a single place. - */ - @SuppressWarnings("unchecked") - public static void convertV2CreateCollectionMapToV1ParamMap(Map<String, Object> v2MapVals) { - // Keys are copied so that map can be modified as keys are looped through. - final Set<String> v2Keys = v2MapVals.keySet().stream().collect(Collectors.toSet()); - for (String key : v2Keys) { - switch (key) { - case V2ApiConstants.PROPERTIES_KEY: - final Map<String, Object> propertiesMap = - (Map<String, Object>) v2MapVals.remove(V2ApiConstants.PROPERTIES_KEY); - flattenMapWithPrefix(propertiesMap, v2MapVals, CollectionAdminParams.PROPERTY_PREFIX); - break; - case ROUTER_KEY: - final Map<String, Object> routerProperties = - (Map<String, Object>) v2MapVals.remove(V2ApiConstants.ROUTER_KEY); - flattenMapWithPrefix(routerProperties, v2MapVals, CollectionAdminParams.ROUTER_PREFIX); - break; - case V2ApiConstants.CONFIG: - v2MapVals.put(CollectionAdminParams.COLL_CONF, v2MapVals.remove(V2ApiConstants.CONFIG)); - break; - case SHARD_NAMES: - final String shardsValue = - String.join(",", (Collection<String>) v2MapVals.remove(SHARD_NAMES)); - v2MapVals.put(SHARDS_PROP, shardsValue); - break; - case V2ApiConstants.SHUFFLE_NODES: - v2MapVals.put( - CollectionAdminParams.CREATE_NODE_SET_SHUFFLE_PARAM, - v2MapVals.remove(V2ApiConstants.SHUFFLE_NODES)); - break; - case V2ApiConstants.NODE_SET: - final Object nodeSetValUncast = v2MapVals.remove(V2ApiConstants.NODE_SET); - if (nodeSetValUncast instanceof String) { - v2MapVals.put(CollectionAdminParams.CREATE_NODE_SET_PARAM, nodeSetValUncast); - } else { - final List<String> nodeSetList = (List<String>) nodeSetValUncast; - final String nodeSetStr = String.join(",", nodeSetList); - v2MapVals.put(CollectionAdminParams.CREATE_NODE_SET_PARAM, nodeSetStr); - } - break; - default: - break; - } + /** + * Convert a map representing the v2 request body into v1-appropriate query-parameters. + * + * <p>Most v2 APIs using the legacy (i.e. non-JAX-RS) framework implement the v2 API by + * restructuring the provided parameters so that the v1 codepath can be called. This utility + * method is provided in pursuit of that usecase. It's not used directly CreateCollectionAPI, + * which uses the JAX-RS framework, but it's kept here so that logic surrounding + * collection-creation parameters can be kept in a single place. + */ + @SuppressWarnings("unchecked") + public static void convertV2CreateCollectionMapToV1ParamMap(Map<String, Object> v2MapVals) { + // Keys are copied so that map can be modified as keys are looped through. + final Set<String> v2Keys = v2MapVals.keySet().stream().collect(Collectors.toSet()); + for (String key : v2Keys) { + switch (key) { + case V2ApiConstants.PROPERTIES_KEY: + final Map<String, Object> propertiesMap = + (Map<String, Object>) v2MapVals.remove(V2ApiConstants.PROPERTIES_KEY); + flattenMapWithPrefix(propertiesMap, v2MapVals, CollectionAdminParams.PROPERTY_PREFIX); + break; + case ROUTER_KEY: + final var routerProperties = + (CreateCollectionRouterProperties) v2MapVals.remove(ROUTER_KEY); + final Map<String, Object> routerPropertiesAsMap = + ((Utils.DelegateReflectWriter) Utils.getReflectWriter(routerProperties)) + .toMap(new HashMap<>()); + flattenMapWithPrefix( + routerPropertiesAsMap, v2MapVals, CollectionAdminParams.ROUTER_PREFIX); + break; + case V2ApiConstants.CONFIG: + v2MapVals.put(CollectionAdminParams.COLL_CONF, v2MapVals.remove(V2ApiConstants.CONFIG)); + break; + case SHARD_NAMES: + final String shardsValue = + String.join(",", (Collection<String>) v2MapVals.remove(SHARD_NAMES)); + v2MapVals.put(SHARDS_PROP, shardsValue); + break; + case V2ApiConstants.SHUFFLE_NODES: + v2MapVals.put( + CollectionAdminParams.CREATE_NODE_SET_SHUFFLE_PARAM, + v2MapVals.remove(V2ApiConstants.SHUFFLE_NODES)); + break; + case V2ApiConstants.NODE_SET: + final Object nodeSetValUncast = v2MapVals.remove(V2ApiConstants.NODE_SET); + if (nodeSetValUncast instanceof String) { + v2MapVals.put(CollectionAdminParams.CREATE_NODE_SET_PARAM, nodeSetValUncast); + } else { + final List<String> nodeSetList = (List<String>) nodeSetValUncast; + final String nodeSetStr = String.join(",", nodeSetList); + v2MapVals.put(CollectionAdminParams.CREATE_NODE_SET_PARAM, nodeSetStr); + } + break; + default: + break; } } } - public static class RouterProperties implements JacksonReflectMapWriter { - @JsonProperty(NAME) - public String name; - - @JsonProperty(FIELD) - public String field; + public static void addToRemoteMessageWithPrefix( + CreateCollectionRequestBody requestBody, Map<String, Object> remoteMessage, String prefix) { + final Map<String, Object> v1Params = + ((Utils.DelegateReflectWriter) Utils.getReflectWriter(requestBody)).toMap(new HashMap<>()); + convertV2CreateCollectionMapToV1ParamMap(v1Params); + for (Map.Entry<String, Object> v1Param : v1Params.entrySet()) { + remoteMessage.put(prefix + v1Param.getKey(), v1Param.getValue()); + } } } diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionBackupAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionBackupAPI.java index 30ad9d7eab9..d37688f2162 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionBackupAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionBackupAPI.java @@ -32,7 +32,7 @@ import static org.apache.solr.common.params.CoreAdminParams.BACKUP_REPOSITORY; import static org.apache.solr.common.params.CoreAdminParams.COMMIT_NAME; import static org.apache.solr.common.params.CoreAdminParams.MAX_NUM_BACKUP_POINTS; import static org.apache.solr.handler.admin.CollectionsHandler.DEFAULT_COLLECTION_OP_TIMEOUT; -import static org.apache.solr.handler.admin.api.CreateCollectionAPI.copyPrefixedPropertiesWithoutPrefix; +import static org.apache.solr.handler.admin.api.CreateCollection.copyPrefixedPropertiesWithoutPrefix; import static org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM; import com.fasterxml.jackson.annotation.JsonProperty; 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 65% 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..180a0a335d4 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; @@ -38,19 +37,15 @@ 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.handler.admin.api.CreateCollection.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,30 @@ 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}) + @Override @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 +93,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 +119,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 +134,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/java/org/apache/solr/handler/admin/api/CreateShardAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateShard.java similarity index 69% rename from solr/core/src/java/org/apache/solr/handler/admin/api/CreateShardAPI.java rename to solr/core/src/java/org/apache/solr/handler/admin/api/CreateShard.java index 41be6e672d7..ffa2f55ef25 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/CreateShard.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; @@ -34,19 +33,15 @@ 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.handler.admin.api.CreateCollection.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.CreateShardApi; +import org.apache.solr.client.api.model.CreateShardRequestBody; import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse; import org.apache.solr.client.solrj.util.SolrIdentifierValidator; import org.apache.solr.common.SolrException; @@ -58,7 +53,6 @@ import org.apache.solr.common.params.CollectionParams; 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.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; @@ -69,23 +63,20 @@ import org.apache.solr.response.SolrQueryResponse; * <p>This API (POST /v2/collections/collectionName/shards {...}) is analogous to the v1 * /admin/collections?action=CREATESHARD command. */ -@Path("/collections/{collectionName}/shards") -public class CreateShardAPI extends AdminAPIBase { +public class CreateShard extends AdminAPIBase implements CreateShardApi { @Inject - public CreateShardAPI( + public CreateShard( CoreContainer coreContainer, SolrQueryRequest solrQueryRequest, SolrQueryResponse solrQueryResponse) { super(coreContainer, solrQueryRequest, solrQueryResponse); } - @POST - @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2}) + @Override @PermissionName(COLL_EDIT_PERM) public SubResponseAccumulatingJerseyResponse createShard( - @PathParam("collectionName") String collectionName, CreateShardRequestBody requestBody) - throws Exception { + String collectionName, CreateShardRequestBody requestBody) throws Exception { final var response = instantiateJerseyResponse(SubResponseAccumulatingJerseyResponse.class); if (requestBody == null) { throw new SolrException( @@ -101,71 +92,34 @@ public class CreateShardAPI extends AdminAPIBase { final ZkNodeProps remoteMessage = createRemoteMessage(resolvedCollectionName, requestBody); submitRemoteMessageAndHandleResponse( - response, - CollectionParams.CollectionAction.CREATESHARD, - remoteMessage, - requestBody.asyncId); + response, CollectionParams.CollectionAction.CREATESHARD, remoteMessage, requestBody.async); return response; } - 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; - - @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(",")); - } + public static CreateShardRequestBody createRequestBodyFromV1Params(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; } + requestBody.waitForFinalState = params.getBool(WAIT_FOR_FINAL_STATE); + requestBody.followAliases = params.getBool(FOLLOW_ALIASES); + requestBody.async = params.get(ASYNC); + requestBody.properties = + copyPrefixedPropertiesWithoutPrefix(params, new HashMap<>(), PROPERTY_PREFIX); + + return requestBody; } public static void invokeFromV1Params( @@ -173,9 +127,8 @@ public class CreateShardAPI extends AdminAPIBase { SolrQueryRequest solrQueryRequest, SolrQueryResponse solrQueryResponse) throws Exception { - final var requestBody = CreateShardRequestBody.fromV1Params(solrQueryRequest.getParams()); - final var createShardApi = - new CreateShardAPI(coreContainer, solrQueryRequest, solrQueryResponse); + final var requestBody = CreateShard.createRequestBodyFromV1Params(solrQueryRequest.getParams()); + final var createShardApi = new CreateShard(coreContainer, solrQueryRequest, solrQueryResponse); final var response = createShardApi.createShard(solrQueryRequest.getParams().get(COLLECTION), requestBody); V2ApiUtils.squashIntoSolrResponseWithoutHeader(solrQueryResponse, response); @@ -201,7 +154,7 @@ public class CreateShardAPI extends AdminAPIBase { 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); + insertIfNotNull(remoteMessage, ASYNC, requestBody.async); if (requestBody.properties != null) { requestBody diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/InstallCoreDataAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/InstallCoreData.java similarity index 73% rename from solr/core/src/java/org/apache/solr/handler/admin/api/InstallCoreDataAPI.java rename to solr/core/src/java/org/apache/solr/handler/admin/api/InstallCoreData.java index c0cd11b3451..a91a0688e0a 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/InstallCoreDataAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/InstallCoreData.java @@ -17,16 +17,11 @@ 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.security.PermissionNameProvider.Name.CORE_EDIT_PERM; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.lang.invoke.MethodHandles; import java.net.URI; -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.InstallCoreDataApi; +import org.apache.solr.client.api.model.InstallCoreDataRequestBody; import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.cloud.CloudDescriptor; import org.apache.solr.cloud.ZkController; @@ -36,25 +31,19 @@ import org.apache.solr.core.SolrCore; import org.apache.solr.core.backup.repository.BackupRepository; import org.apache.solr.handler.RestoreCore; import org.apache.solr.handler.admin.CoreAdminHandler; -import org.apache.solr.jersey.JacksonReflectMapWriter; import org.apache.solr.jersey.PermissionName; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** - * v2 implementation of the "Install Core Data" Core-Admin API + * V2 API implementation of the "Install Core Data" Core-Admin API * * <p>This is an internal API intended for use only by the Collection Admin "Install Shard Data" * API. */ -@Path("/cores/{coreName}/install") -public class InstallCoreDataAPI extends CoreAdminAPIBase { +public class InstallCoreData extends CoreAdminAPIBase implements InstallCoreDataApi { - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - public InstallCoreDataAPI( + public InstallCoreData( CoreContainer coreContainer, CoreAdminHandler.CoreAdminAsyncTracker coreAdminAsyncTracker, SolrQueryRequest req, @@ -62,11 +51,9 @@ public class InstallCoreDataAPI extends CoreAdminAPIBase { super(coreContainer, coreAdminAsyncTracker, req, rsp); } - @POST - @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2}) + @Override @PermissionName(CORE_EDIT_PERM) - public SolrJerseyResponse installCoreData( - @PathParam("coreName") String coreName, InstallCoreDataRequestBody requestBody) + public SolrJerseyResponse installCoreData(String coreName, InstallCoreDataRequestBody requestBody) throws Exception { final SolrJerseyResponse response = instantiateJerseyResponse(SolrJerseyResponse.class); @@ -118,18 +105,4 @@ public class InstallCoreDataAPI extends CoreAdminAPIBase { return response; } - - public static class InstallCoreDataRequestBody implements JacksonReflectMapWriter { - // Expected to point to an index directory (e.g. data/techproducts_shard1_replica_n1/data/index) - // for a single core that has previously been uploaded to the backup repository previously - // uploaded to the backup repository. - @JsonProperty("location") - public String location; - - @JsonProperty("repository") - public String repository; - - @JsonProperty("async") - public String asyncId; - } } diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/InstallShardDataAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/InstallShardData.java similarity index 78% rename from solr/core/src/java/org/apache/solr/handler/admin/api/InstallShardDataAPI.java rename to solr/core/src/java/org/apache/solr/handler/admin/api/InstallShardData.java index 0b313b02712..05df6a2b15e 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/InstallShardDataAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/InstallShardData.java @@ -17,18 +17,13 @@ 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.handler.admin.CollectionsHandler.DEFAULT_COLLECTION_OP_TIMEOUT; import static org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.lang.invoke.MethodHandles; import java.util.HashMap; 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.InstallShardDataApi; +import org.apache.solr.client.api.model.InstallShardDataRequestBody; import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.client.solrj.SolrResponse; import org.apache.solr.cloud.api.collections.InstallShardDataCmd; @@ -40,43 +35,32 @@ import org.apache.solr.common.cloud.ZkNodeProps; import org.apache.solr.common.params.CollectionParams; import org.apache.solr.core.CoreContainer; import org.apache.solr.handler.admin.CollectionsHandler; -import org.apache.solr.jersey.JacksonReflectMapWriter; import org.apache.solr.jersey.PermissionName; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; import org.apache.zookeeper.common.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** - * A V2 API that allows users to import an index constructed offline into a shard of their - * collection + * V2 API implementation allowing users to import offline-constructed index into a shard. * * <p>Particularly useful for installing (per-shard) indices constructed offline into a SolrCloud * deployment. Callers are required to put the collection into read-only mode prior to installing * data into any shards of that collection, and should exit read only mode when completed. */ -@Path("/collections/{collName}/shards/{shardName}/install") -public class InstallShardDataAPI extends AdminAPIBase { - - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); +public class InstallShardData extends AdminAPIBase implements InstallShardDataApi { @Inject - public InstallShardDataAPI( + public InstallShardData( CoreContainer coreContainer, SolrQueryRequest solrQueryRequest, SolrQueryResponse solrQueryResponse) { super(coreContainer, solrQueryRequest, solrQueryResponse); } - @POST - @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2}) + @Override @PermissionName(COLL_EDIT_PERM) public SolrJerseyResponse installShardData( - @PathParam("collName") String collName, - @PathParam("shardName") String shardName, - InstallShardRequestBody requestBody) - throws Exception { + String collName, String shardName, InstallShardDataRequestBody requestBody) throws Exception { final SolrJerseyResponse response = instantiateJerseyResponse(SolrJerseyResponse.class); final CoreContainer coreContainer = fetchAndValidateZooKeeperAwareCoreContainer(); recordCollectionForLogAndTracing(collName, solrQueryRequest); @@ -130,28 +114,17 @@ public class InstallShardDataAPI extends AdminAPIBase { } public static ZkNodeProps createRemoteMessage( - String collectionName, String shardName, InstallShardRequestBody requestBody) { + String collectionName, String shardName, InstallShardDataRequestBody requestBody) { final InstallShardDataCmd.RemoteMessage messageTyped = new InstallShardDataCmd.RemoteMessage(); messageTyped.collection = collectionName; messageTyped.shard = shardName; if (requestBody != null) { messageTyped.location = requestBody.location; messageTyped.repository = requestBody.repository; - messageTyped.asyncId = requestBody.asyncId; + messageTyped.asyncId = requestBody.async; } messageTyped.validate(); return new ZkNodeProps(messageTyped.toMap(new HashMap<>())); } - - public static class InstallShardRequestBody implements JacksonReflectMapWriter { - @JsonProperty(defaultValue = "location", required = true) - public String location; - - @JsonProperty("repository") - public String repository; - - @JsonProperty("async") - public String asyncId; - } } diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCollectionAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCollectionAPI.java index 1809d83f6b9..b950abf44ec 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCollectionAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCollectionAPI.java @@ -46,6 +46,7 @@ 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.model.CreateCollectionRequestBody; import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse; import org.apache.solr.client.solrj.SolrResponse; @@ -121,8 +122,8 @@ public class RestoreCollectionAPI extends BackupAPIBase { final var createRequestBody = requestBody.createCollectionParams; if (createRequestBody != null) { - CreateCollectionAPI.populateDefaultsIfNecessary(coreContainer, createRequestBody); - createRequestBody.validate(); + CreateCollection.populateDefaultsIfNecessary(coreContainer, createRequestBody); + CreateCollection.validateRequestBody(createRequestBody); if (Boolean.FALSE.equals(createRequestBody.createReplicas)) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, @@ -160,7 +161,7 @@ public class RestoreCollectionAPI extends BackupAPIBase { // RESTORE only supports a subset of collection-creation params, so filter by those when // constructing the remote message remoteMessage.remove("create-collection"); - CreateCollectionAPI.createRemoteMessage(createReqBody).getProperties().entrySet().stream() + CreateCollection.createRemoteMessage(createReqBody).getProperties().entrySet().stream() .filter( e -> CREATE_PARAM_ALLOWLIST.contains(e.getKey()) @@ -204,7 +205,7 @@ public class RestoreCollectionAPI extends BackupAPIBase { @JsonProperty public Integer backupId; @JsonProperty(CREATE_COLLECTION_KEY) - public CreateCollectionAPI.CreateCollectionRequestBody createCollectionParams; + public CreateCollectionRequestBody createCollectionParams; @JsonProperty public String async; @@ -217,7 +218,7 @@ public class RestoreCollectionAPI extends BackupAPIBase { restoreBody.async = solrParams.get(ASYNC); restoreBody.createCollectionParams = - CreateCollectionAPI.CreateCollectionRequestBody.fromV1Params(solrParams, false); + CreateCollection.createRequestBodyFromV1Params(solrParams, false); return restoreBody; } diff --git a/solr/core/src/java/org/apache/solr/jersey/PostRequestLoggingFilter.java b/solr/core/src/java/org/apache/solr/jersey/PostRequestLoggingFilter.java index 3925b297d6a..a7a6632fa65 100644 --- a/solr/core/src/java/org/apache/solr/jersey/PostRequestLoggingFilter.java +++ b/solr/core/src/java/org/apache/solr/jersey/PostRequestLoggingFilter.java @@ -41,6 +41,7 @@ import javax.ws.rs.core.MultivaluedMap; import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.common.util.CollectionUtil; import org.apache.solr.common.util.StrUtils; +import org.apache.solr.common.util.Utils; import org.apache.solr.core.SolrCore; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.servlet.HttpSolrCall; @@ -141,19 +142,23 @@ public class PostRequestLoggingFilter implements ContainerResponseFilter { return "{}"; } - if (!(requestContext.getProperty(DESERIALIZED_REQUEST_BODY_KEY) - instanceof JacksonReflectMapWriter)) { - log.warn( - "Encountered unexpected request-body type {} for request {}; only {} expected.", - requestContext.getProperty(DESERIALIZED_REQUEST_BODY_KEY).getClass().getName(), - requestContext.getUriInfo().getPath(), - JacksonReflectMapWriter.class.getName()); - return "{}"; + final Object deserializedBody = requestContext.getProperty(DESERIALIZED_REQUEST_BODY_KEY); + if (deserializedBody instanceof JacksonReflectMapWriter) { + return ((JacksonReflectMapWriter) requestContext.getProperty(DESERIALIZED_REQUEST_BODY_KEY)) + .jsonStr() + .replace("\n", ""); + } + + final Object reflectWritable = Utils.getReflectWriter(deserializedBody); + if (reflectWritable instanceof Utils.DelegateReflectWriter) { + return Utils.toJSONString(reflectWritable).replaceAll("\n", ""); } - return ((JacksonReflectMapWriter) requestContext.getProperty(DESERIALIZED_REQUEST_BODY_KEY)) - .jsonStr() - .replace("\n", ""); + log.warn( + "No reflection data found for request-body type {} for request {}; omitting request-body details from logging", + deserializedBody.getClass().getName(), + requestContext.getUriInfo().getPath()); + return "{}"; } public static String filterAndStringifyQueryParameters( diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/CreateAliasAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/CreateAliasAPITest.java index ce4b5b51544..1e97d61a543 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/CreateAliasAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/CreateAliasAPITest.java @@ -24,6 +24,7 @@ import static org.hamcrest.Matchers.containsString; import java.util.List; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.api.model.CreateCollectionRequestBody; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.ModifiableSolrParams; import org.hamcrest.MatcherAssert; @@ -95,7 +96,7 @@ public class CreateAliasAPITest extends SolrTestCaseJ4 { final var categoryRouter = new CreateAliasAPI.CategoryRoutedAliasProperties(); categoryRouter.field = "someField"; requestBody.routers = List.of(categoryRouter); - final var createParams = new CreateCollectionAPI.CreateCollectionRequestBody(); + final var createParams = new CreateCollectionRequestBody(); createParams.numShards = 3; requestBody.collCreationParameters = createParams; @@ -112,7 +113,7 @@ public class CreateAliasAPITest extends SolrTestCaseJ4 { final var categoryRouter = new CreateAliasAPI.CategoryRoutedAliasProperties(); categoryRouter.field = "someField"; requestBody.routers = List.of(categoryRouter); - final var createParams = new CreateCollectionAPI.CreateCollectionRequestBody(); + final var createParams = new CreateCollectionRequestBody(); createParams.numShards = 3; createParams.config = "someConfig"; // Not allowed since routed-aliases-created collections have semantically meaningful names @@ -130,7 +131,7 @@ public class CreateAliasAPITest extends SolrTestCaseJ4 { public void testReportsErrorIfCategoryRoutedAliasDoesntSpecifyAllRequiredParameters() { final var requestBody = new CreateAliasAPI.CreateAliasRequestBody(); requestBody.name = "validName"; - final var createParams = new CreateCollectionAPI.CreateCollectionRequestBody(); + final var createParams = new CreateCollectionRequestBody(); createParams.numShards = 3; createParams.config = "someConfig"; requestBody.collCreationParameters = createParams; @@ -212,7 +213,7 @@ public class CreateAliasAPITest extends SolrTestCaseJ4 { final var categoryRouter = new CreateAliasAPI.CategoryRoutedAliasProperties(); categoryRouter.field = "someField"; requestBody.routers = List.of(categoryRouter); - final var createParams = new CreateCollectionAPI.CreateCollectionRequestBody(); + final var createParams = new CreateCollectionRequestBody(); createParams.numShards = 3; createParams.config = "someConfig"; requestBody.collCreationParameters = createParams; @@ -239,7 +240,7 @@ public class CreateAliasAPITest extends SolrTestCaseJ4 { timeRouter.interval = "+1MONTH"; timeRouter.maxFutureMs = 123456L; requestBody.routers = List.of(timeRouter); - final var createParams = new CreateCollectionAPI.CreateCollectionRequestBody(); + final var createParams = new CreateCollectionRequestBody(); createParams.numShards = 3; createParams.config = "someConfig"; requestBody.collCreationParameters = createParams; @@ -271,7 +272,7 @@ public class CreateAliasAPITest extends SolrTestCaseJ4 { final var categoryRouter = new CreateAliasAPI.CategoryRoutedAliasProperties(); categoryRouter.field = "someField"; requestBody.routers = List.of(timeRouter, categoryRouter); - final var createParams = new CreateCollectionAPI.CreateCollectionRequestBody(); + final var createParams = new CreateCollectionRequestBody(); createParams.numShards = 3; createParams.config = "someConfig"; requestBody.collCreationParameters = createParams; @@ -297,7 +298,7 @@ public class CreateAliasAPITest extends SolrTestCaseJ4 { CreateAliasAPI.RoutedAliasProperties router) { final var requestBody = new CreateAliasAPI.CreateAliasRequestBody(); requestBody.name = "validName"; - final var createParams = new CreateCollectionAPI.CreateCollectionRequestBody(); + final var createParams = new CreateCollectionRequestBody(); createParams.numShards = 3; createParams.config = "someConfig"; requestBody.collCreationParameters = createParams; diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/CreateCollectionAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/CreateCollectionAPITest.java index c5eb27f0835..80a2200131a 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/CreateCollectionAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/CreateCollectionAPITest.java @@ -37,11 +37,13 @@ import static org.apache.solr.common.params.CoreAdminParams.NAME; import java.util.List; import java.util.Map; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.api.model.CreateCollectionRequestBody; +import org.apache.solr.client.api.model.CreateCollectionRouterProperties; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.ModifiableSolrParams; import org.junit.Test; -/** Unit tests for {@link CreateCollectionAPI}. */ +/** Unit tests for {@link CreateCollection}. */ public class CreateCollectionAPITest extends SolrTestCaseJ4 { @Test @@ -50,7 +52,7 @@ public class CreateCollectionAPITest extends SolrTestCaseJ4 { expectThrows( SolrException.class, () -> { - final var api = new CreateCollectionAPI(null, null, null); + final var api = new CreateCollection(null, null, null); api.createCollection(null); }); @@ -61,7 +63,7 @@ public class CreateCollectionAPITest extends SolrTestCaseJ4 { @Test public void testReportsErrorIfReplicationFactorAndNrtReplicasConflict() { // Valid request body... - final var requestBody = new CreateCollectionAPI.CreateCollectionRequestBody(); + final var requestBody = new CreateCollectionRequestBody(); requestBody.name = "someName"; requestBody.config = "someConfig"; // ...except for a replicationFactor and nrtReplicas conflicting @@ -72,7 +74,7 @@ public class CreateCollectionAPITest extends SolrTestCaseJ4 { expectThrows( SolrException.class, () -> { - requestBody.validate(); + CreateCollection.validateRequestBody(requestBody); }); assertEquals(400, thrown.code()); @@ -83,7 +85,7 @@ public class CreateCollectionAPITest extends SolrTestCaseJ4 { @Test public void testReportsErrorIfCollectionNameInvalid() { - final var requestBody = new CreateCollectionAPI.CreateCollectionRequestBody(); + final var requestBody = new CreateCollectionRequestBody(); requestBody.name = "$invalid@collection+name"; requestBody.config = "someConfig"; @@ -91,7 +93,7 @@ public class CreateCollectionAPITest extends SolrTestCaseJ4 { expectThrows( SolrException.class, () -> { - requestBody.validate(); + CreateCollection.validateRequestBody(requestBody); }); assertEquals(400, thrown.code()); @@ -103,7 +105,7 @@ public class CreateCollectionAPITest extends SolrTestCaseJ4 { @Test public void testReportsErrorIfShardNamesInvalid() { // Valid request body... - final var requestBody = new CreateCollectionAPI.CreateCollectionRequestBody(); + final var requestBody = new CreateCollectionRequestBody(); requestBody.name = "someName"; requestBody.config = "someConfig"; // ...except for a bad shard name @@ -113,7 +115,7 @@ public class CreateCollectionAPITest extends SolrTestCaseJ4 { expectThrows( SolrException.class, () -> { - requestBody.validate(); + CreateCollection.validateRequestBody(requestBody); }); assertEquals(400, thrown.code()); @@ -124,7 +126,7 @@ public class CreateCollectionAPITest extends SolrTestCaseJ4 { @Test public void testCreateRemoteMessageAllProperties() { - final var requestBody = new CreateCollectionAPI.CreateCollectionRequestBody(); + final var requestBody = new CreateCollectionRequestBody(); requestBody.name = "someName"; requestBody.replicationFactor = 123; requestBody.config = "someConfig"; @@ -137,13 +139,13 @@ public class CreateCollectionAPITest extends SolrTestCaseJ4 { requestBody.alias = "someAliasName"; requestBody.properties = Map.of("propName", "propValue"); requestBody.async = "someAsyncId"; - requestBody.router = new CreateCollectionAPI.RouterProperties(); + requestBody.router = new CreateCollectionRouterProperties(); requestBody.router.name = "someRouterName"; requestBody.router.field = "someField"; requestBody.nodeSet = List.of("node1", "node2"); requestBody.shuffleNodes = false; - final var remoteMessage = CreateCollectionAPI.createRemoteMessage(requestBody).getProperties(); + final var remoteMessage = CreateCollection.createRemoteMessage(requestBody).getProperties(); assertEquals("create", remoteMessage.get(QUEUE_OPERATION)); assertEquals("true", remoteMessage.get("fromApi")); @@ -168,11 +170,11 @@ public class CreateCollectionAPITest extends SolrTestCaseJ4 { @Test public void testNoReplicaCreationMessage() { - final var requestBody = new CreateCollectionAPI.CreateCollectionRequestBody(); + final var requestBody = new CreateCollectionRequestBody(); requestBody.name = "someName"; requestBody.createReplicas = false; - final var remoteMessage = CreateCollectionAPI.createRemoteMessage(requestBody).getProperties(); + final var remoteMessage = CreateCollection.createRemoteMessage(requestBody).getProperties(); assertEquals("create", remoteMessage.get(QUEUE_OPERATION)); assertEquals("someName", remoteMessage.get(NAME)); @@ -196,8 +198,7 @@ public class CreateCollectionAPITest extends SolrTestCaseJ4 { solrParams.set("router.field", "someField"); solrParams.set("property.somePropName", "somePropValue"); - final var v2RequestBody = - CreateCollectionAPI.CreateCollectionRequestBody.fromV1Params(solrParams, true); + final var v2RequestBody = CreateCollection.createRequestBodyFromV1Params(solrParams, true); assertEquals("someName", v2RequestBody.name); assertEquals(Integer.valueOf(123), v2RequestBody.numShards); @@ -223,8 +224,7 @@ public class CreateCollectionAPITest extends SolrTestCaseJ4 { solrParams.set(NAME, "someName"); solrParams.set(CREATE_NODE_SET, "EMPTY"); - final var v2RequestBody = - CreateCollectionAPI.CreateCollectionRequestBody.fromV1Params(solrParams, true); + final var v2RequestBody = CreateCollection.createRequestBodyFromV1Params(solrParams, true); assertEquals("someName", v2RequestBody.name); assertEquals(Boolean.FALSE, v2RequestBody.createReplicas); 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/handler/admin/api/CreateShardAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/CreateShardAPITest.java index 07b71d8a805..bb89fd326b5 100644 --- 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 @@ -33,12 +33,13 @@ import static org.hamcrest.Matchers.containsString; import java.util.List; import java.util.Map; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.api.model.CreateShardRequestBody; 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} */ +/** Unit tests for {@link CreateShard} */ public class CreateShardAPITest extends SolrTestCaseJ4 { @Test @@ -47,7 +48,7 @@ public class CreateShardAPITest extends SolrTestCaseJ4 { expectThrows( SolrException.class, () -> { - final var api = new CreateShardAPI(null, null, null); + final var api = new CreateShard(null, null, null); api.createShard("someCollName", null); }); @@ -57,13 +58,13 @@ public class CreateShardAPITest extends SolrTestCaseJ4 { @Test public void testReportsErrorIfCollectionNameMissing() { - final var requestBody = new CreateShardAPI.CreateShardRequestBody(); + final var requestBody = new CreateShardRequestBody(); requestBody.shardName = "someShardName"; final SolrException thrown = expectThrows( SolrException.class, () -> { - final var api = new CreateShardAPI(null, null, null); + final var api = new CreateShard(null, null, null); api.createShard(null, requestBody); }); @@ -73,13 +74,13 @@ public class CreateShardAPITest extends SolrTestCaseJ4 { @Test public void testReportsErrorIfShardNameMissing() { - final var requestBody = new CreateShardAPI.CreateShardRequestBody(); + final var requestBody = new CreateShardRequestBody(); requestBody.shardName = null; final SolrException thrown = expectThrows( SolrException.class, () -> { - final var api = new CreateShardAPI(null, null, null); + final var api = new CreateShard(null, null, null); api.createShard("someCollectionName", requestBody); }); @@ -89,13 +90,13 @@ public class CreateShardAPITest extends SolrTestCaseJ4 { @Test public void testReportsErrorIfShardNameIsInvalid() { - final var requestBody = new CreateShardAPI.CreateShardRequestBody(); + final var requestBody = new CreateShardRequestBody(); requestBody.shardName = "invalid$shard@name"; final SolrException thrown = expectThrows( SolrException.class, () -> { - final var api = new CreateShardAPI(null, null, null); + final var api = new CreateShard(null, null, null); api.createShard("someCollectionName", requestBody); }); @@ -106,7 +107,7 @@ public class CreateShardAPITest extends SolrTestCaseJ4 { @Test public void testCreateRemoteMessageAllProperties() { - final var requestBody = new CreateShardAPI.CreateShardRequestBody(); + final var requestBody = new CreateShardRequestBody(); requestBody.shardName = "someShardName"; requestBody.replicationFactor = 123; requestBody.nrtReplicas = 123; @@ -116,11 +117,11 @@ public class CreateShardAPITest extends SolrTestCaseJ4 { requestBody.nodeSet = List.of("node1", "node2"); requestBody.waitForFinalState = true; requestBody.followAliases = true; - requestBody.asyncId = "someAsyncId"; + requestBody.async = "someAsyncId"; requestBody.properties = Map.of("propName1", "propVal1", "propName2", "propVal2"); final var remoteMessage = - CreateShardAPI.createRemoteMessage("someCollectionName", requestBody).getProperties(); + CreateShard.createRemoteMessage("someCollectionName", requestBody).getProperties(); assertEquals(13, remoteMessage.size()); assertEquals("createshard", remoteMessage.get(QUEUE_OPERATION)); @@ -154,7 +155,7 @@ public class CreateShardAPITest extends SolrTestCaseJ4 { v1Params.add("property.propName1", "propVal1"); v1Params.add("property.propName2", "propVal2"); - final var requestBody = CreateShardAPI.CreateShardRequestBody.fromV1Params(v1Params); + final var requestBody = CreateShard.createRequestBodyFromV1Params(v1Params); assertEquals("someShardName", requestBody.shardName); assertEquals(Integer.valueOf(123), requestBody.replicationFactor); @@ -165,7 +166,7 @@ public class CreateShardAPITest extends SolrTestCaseJ4 { assertEquals(List.of("node1", "node2"), requestBody.nodeSet); 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/handler/admin/api/RestoreCollectionAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/RestoreCollectionAPITest.java index 52321b3f3c8..d4a303f2599 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/RestoreCollectionAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/RestoreCollectionAPITest.java @@ -35,6 +35,7 @@ import static org.hamcrest.Matchers.containsString; import java.util.List; import java.util.Map; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.api.model.CreateCollectionRequestBody; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.ModifiableSolrParams; import org.hamcrest.MatcherAssert; @@ -136,7 +137,7 @@ public class RestoreCollectionAPITest extends SolrTestCaseJ4 { requestBody.backupId = 123; requestBody.repository = "someRepositoryName"; requestBody.async = "someAsyncId"; - final var createParams = new CreateCollectionAPI.CreateCollectionRequestBody(); + final var createParams = new CreateCollectionRequestBody(); requestBody.createCollectionParams = createParams; createParams.config = "someConfig"; createParams.nrtReplicas = 123; 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); diff --git a/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/GCSInstallShardTest.java b/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/GCSInstallShardTest.java index 51dc59d2ea0..3d0d30262ed 100644 --- a/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/GCSInstallShardTest.java +++ b/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/GCSInstallShardTest.java @@ -20,6 +20,7 @@ package org.apache.solr.gcs; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering; import org.apache.lucene.tests.util.LuceneTestCase; import org.apache.solr.cloud.api.collections.AbstractInstallShardTest; +import org.apache.solr.handler.admin.api.InstallShardData; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -27,7 +28,7 @@ import org.junit.BeforeClass; * Tests validating that the 'Install Shard API' works when used with {@link GCSBackupRepository} * * @see org.apache.solr.cloud.api.collections.AbstractInstallShardTest - * @see org.apache.solr.handler.admin.api.InstallShardDataAPI + * @see InstallShardData */ // Backups do checksum validation against a footer value not present in 'SimpleText' @LuceneTestCase.SuppressCodecs({"SimpleText"}) diff --git a/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3InstallShardTest.java b/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3InstallShardTest.java index 303e414d84e..11a58e2100a 100644 --- a/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3InstallShardTest.java +++ b/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3InstallShardTest.java @@ -21,6 +21,7 @@ import com.adobe.testing.s3mock.junit4.S3MockRule; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering; import org.apache.lucene.tests.util.LuceneTestCase; import org.apache.solr.cloud.api.collections.AbstractInstallShardTest; +import org.apache.solr.handler.admin.api.InstallShardData; import org.junit.BeforeClass; import org.junit.ClassRule; import software.amazon.awssdk.regions.Region; @@ -29,7 +30,7 @@ import software.amazon.awssdk.regions.Region; * Tests validating that the 'Install Shard API' works when used with {@link S3BackupRepository} * * @see org.apache.solr.cloud.api.collections.AbstractInstallShardTest - * @see org.apache.solr.handler.admin.api.InstallShardDataAPI + * @see InstallShardData */ // Backups do checksum validation against a footer value not present in 'SimpleText' @LuceneTestCase.SuppressCodecs({"SimpleText"}) diff --git a/solr/solrj/src/resources/java-template/api.mustache b/solr/solrj/src/resources/java-template/api.mustache index 93108eae513..7f40f2332d7 100644 --- a/solr/solrj/src/resources/java-template/api.mustache +++ b/solr/solrj/src/resources/java-template/api.mustache @@ -34,13 +34,26 @@ import org.apache.solr.client.solrj.JacksonContentWriter; import org.apache.solr.client.solrj.request.RequestWriter.ContentWriter; import org.apache.solr.client.solrj.impl.InputStreamResponseParser; import org.apache.solr.client.solrj.ResponseParser; -{{#operations}} -{{#operation}} + +{{! Covers all top-level request/response model classes, but not necessarily any types nested in those classes }} {{#imports}} -import {{modelPackage}}.{{.}}; +import {{import}}; {{/imports}} + +{{! Imports any model types nested in request body POJOs }} +{{#operations}} +{{#operation}} +{{#bodyParam}} +{{#vars}} +{{#isModel}} +import {{modelPackage}}.{{dataType}}; +{{/isModel}} +{{/vars}} +{{/bodyParam}} {{/operation}} +{{/operations}} +{{#operations}} // WARNING: This class is generated from a Mustache template; any intended // changes should be made to the underlying template and not this file directly. diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractInstallShardTest.java b/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractInstallShardTest.java index 8bcd85077b7..497e8e62cf8 100644 --- a/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractInstallShardTest.java +++ b/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractInstallShardTest.java @@ -48,6 +48,7 @@ import org.apache.solr.core.CoreDescriptor; import org.apache.solr.core.DirectoryFactory; import org.apache.solr.core.SolrCore; import org.apache.solr.core.backup.repository.BackupRepository; +import org.apache.solr.handler.admin.api.InstallShardData; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -62,7 +63,7 @@ import org.slf4j.LoggerFactory; * repository. This base-class will populate that backup repository all data necessary for these * tests. * - * @see org.apache.solr.handler.admin.api.InstallShardDataAPI + * @see InstallShardData */ public abstract class AbstractInstallShardTest extends SolrCloudTestCase {
