From 968aac2fa997a8a671764ae292def10c3b08f656 Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Tue, 12 Nov 2024 10:26:32 -0800 Subject: [PATCH] SOLR-16825: Migrate v2 API definitions to 'api' module, pt 8 (#2833) SOLR-16825 added a new gradle module, 'api', which holds v2 API definitions as interfaces. This allows us to generate an OAS (and other artifacts) as a part of the solrj build. But these artifacts only cover the v2 APIs present in the 'api' module. This commit moves v2 API defining annotations to new interfaces in the 'api' module for several "schema", "replication", and "logging" APIs as well as 'create-alias'. This APIs are all now reflected in our OAS and generated artifacts. --- .../client/api/endpoint/CreateAliasApi.java | 32 +++ .../client/api/endpoint/GetSchemaApi.java | 69 ++++++ .../client/api/endpoint/NodeLoggingApis.java | 61 ++++++ .../client/api/endpoint/ReplicationApis.java | 50 +++++ .../model/CategoryRoutedAliasProperties.java | 27 +++ .../api/model/CreateAliasRequestBody.java | 42 ++++ .../client/api/model/FileListResponse.java | 38 ++++ .../solr/client/api/model/FileMetaData.java | 42 ++++ .../api/model/IndexVersionResponse.java | 40 ++++ .../client/api/model/ListLevelsResponse.java | 26 +++ .../solr/client/api/model/LogLevelChange.java | 32 +++ .../solr/client/api/model/LogLevelInfo.java | 39 ++++ .../solr/client/api/model/LogMessageInfo.java | 35 +++ .../client/api/model/LogMessagesResponse.java | 29 +++ .../client/api/model/LoggingResponse.java | 25 +++ .../api/model/RoutedAliasProperties.java | 31 +++ .../SchemaGetDynamicFieldInfoResponse.java | 27 +++ .../api/model/SchemaGetFieldInfoResponse.java | 27 +++ .../model/SchemaGetFieldTypeInfoResponse.java | 27 +++ .../model/SchemaListCopyFieldsResponse.java | 25 +++ .../SchemaListDynamicFieldsResponse.java | 25 +++ .../model/SchemaListFieldTypesResponse.java | 25 +++ .../api/model/SchemaListFieldsResponse.java | 25 +++ .../api/model/SetThresholdRequestBody.java | 31 +++ .../api/model/TimeRoutedAliasProperties.java | 41 ++++ .../solr/client/api/util/Constants.java | 2 + .../client/api/util/CoreApiParameters.java | 40 ++++ .../client/api/util/StoreApiParameters.java | 6 +- .../org/apache/solr/handler/IndexFetcher.java | 6 +- .../solr/handler/ReplicationHandler.java | 25 +-- .../apache/solr/handler/SchemaHandler.java | 18 +- .../handler/admin/CollectionsHandler.java | 10 +- .../solr/handler/admin/LoggingHandler.java | 19 +- ...deLoggingAPI.java => CoreReplication.java} | 44 ++-- .../handler/admin/api/CoreReplicationAPI.java | 138 ------------ .../{CreateAliasAPI.java => CreateAlias.java} | 200 ++++++++---------- ...chemaFieldAPI.java => GetSchemaField.java} | 93 ++------ .../{NodeLoggingAPI.java => NodeLogging.java} | 140 +++--------- .../handler/admin/api/ReplicationAPIBase.java | 21 +- .../admin/api/CoreReplicationAPITest.java | 16 +- .../handler/admin/api/CreateAliasAPITest.java | 101 +++++---- .../admin/api/GetSchemaFieldsAPITest.java | 12 +- .../handler/admin/api/NodeLoggingAPITest.java | 27 +-- .../src/resources/java-template/api.mustache | 27 +++ 44 files changed, 1236 insertions(+), 580 deletions(-) create mode 100644 solr/api/src/java/org/apache/solr/client/api/endpoint/CreateAliasApi.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/endpoint/NodeLoggingApis.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/endpoint/ReplicationApis.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/CategoryRoutedAliasProperties.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/CreateAliasRequestBody.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/FileListResponse.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/FileMetaData.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/IndexVersionResponse.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/ListLevelsResponse.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/LogLevelChange.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/LogLevelInfo.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/LogMessageInfo.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/LogMessagesResponse.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/LoggingResponse.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/RoutedAliasProperties.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/SchemaGetDynamicFieldInfoResponse.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/SchemaGetFieldInfoResponse.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/SchemaGetFieldTypeInfoResponse.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/SchemaListCopyFieldsResponse.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/SchemaListDynamicFieldsResponse.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/SchemaListFieldTypesResponse.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/SchemaListFieldsResponse.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/SetThresholdRequestBody.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/TimeRoutedAliasProperties.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/util/CoreApiParameters.java rename solr/core/src/java/org/apache/solr/handler/admin/api/{V2NodeLoggingAPI.java => CoreReplication.java} (51%) delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplicationAPI.java rename solr/core/src/java/org/apache/solr/handler/admin/api/{CreateAliasAPI.java => CreateAlias.java} (69%) rename solr/core/src/java/org/apache/solr/handler/admin/api/{GetSchemaFieldAPI.java => GetSchemaField.java} (74%) rename solr/core/src/java/org/apache/solr/handler/admin/api/{NodeLoggingAPI.java => NodeLogging.java} (57%) diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/CreateAliasApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/CreateAliasApi.java new file mode 100644 index 00000000000..78b9b4376c0 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/CreateAliasApi.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.client.api.endpoint; + +import io.swagger.v3.oas.annotations.Operation; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import org.apache.solr.client.api.model.CreateAliasRequestBody; +import org.apache.solr.client.api.model.SolrJerseyResponse; + +@Path("/aliases") +public interface CreateAliasApi { + @POST + @Operation( + summary = "Create a traditional or 'routed' alias", + tags = {"aliases"}) + SolrJerseyResponse createAlias(CreateAliasRequestBody requestBody) throws Exception; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/GetSchemaApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/GetSchemaApi.java index 8cec1d02f7a..119bf9d19ce 100644 --- a/solr/api/src/java/org/apache/solr/client/api/endpoint/GetSchemaApi.java +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/GetSchemaApi.java @@ -22,8 +22,16 @@ import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; import jakarta.ws.rs.QueryParam; +import org.apache.solr.client.api.model.SchemaGetDynamicFieldInfoResponse; +import org.apache.solr.client.api.model.SchemaGetFieldInfoResponse; +import org.apache.solr.client.api.model.SchemaGetFieldTypeInfoResponse; import org.apache.solr.client.api.model.SchemaInfoResponse; +import org.apache.solr.client.api.model.SchemaListCopyFieldsResponse; +import org.apache.solr.client.api.model.SchemaListDynamicFieldsResponse; +import org.apache.solr.client.api.model.SchemaListFieldTypesResponse; +import org.apache.solr.client.api.model.SchemaListFieldsResponse; import org.apache.solr.client.api.model.SchemaNameResponse; import org.apache.solr.client.api.model.SchemaSimilarityResponse; import org.apache.solr.client.api.model.SchemaUniqueKeyResponse; @@ -34,6 +42,67 @@ @Path(INDEX_PATH_PREFIX + "/schema") public interface GetSchemaApi { + @Path(INDEX_PATH_PREFIX + "/schema") + interface Fields { + + @GET + @Path("/fields") + @StoreApiParameters + @Operation( + summary = "List all non-dynamic fields in the schema of the specified core or collection", + tags = {"schema"}) + SchemaListFieldsResponse listSchemaFields(); + + @GET + @Path("/fields/{fieldName}") + @StoreApiParameters + @Operation( + summary = "Get detailed info about a single non-dynamic field", + tags = {"schema"}) + SchemaGetFieldInfoResponse getFieldInfo(@PathParam("fieldName") String fieldName); + + @GET + @Path("/copyfields") + @StoreApiParameters + @Operation( + summary = "List all copy-fields in the schema of the specified core or collection", + tags = {"schema"}) + SchemaListCopyFieldsResponse listCopyFields(); + + @GET + @Path("/dynamicfields") + @StoreApiParameters + @Operation( + summary = "List all dynamic-fields in the schema of the specified core or collection", + tags = {"schema"}) + SchemaListDynamicFieldsResponse listDynamicFields(); + + @GET + @Path("/dynamicfields/{fieldName}") + @StoreApiParameters + @Operation( + summary = "Get detailed info about a single dynamic field", + tags = {"schema"}) + SchemaGetDynamicFieldInfoResponse getDynamicFieldInfo(@PathParam("fieldName") String fieldName); + + @GET + @Path("/fieldtypes") + @StoreApiParameters + @Operation( + summary = "List all field types in the schema used by the specified core or collection", + tags = {"schema"}) + SchemaListFieldTypesResponse listSchemaFieldTypes(); + + @GET + @Path("/fieldtypes/{fieldTypeName}") + @StoreApiParameters + @Operation( + summary = "Get detailed info about a single field type", + tags = {"schema"}) + SchemaGetFieldTypeInfoResponse getFieldTypeInfo( + @PathParam("fieldTypeName") String fieldTypeName); + } + @GET @StoreApiParameters @Operation( diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/NodeLoggingApis.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/NodeLoggingApis.java new file mode 100644 index 00000000000..c5b5cd95807 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/NodeLoggingApis.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.client.api.endpoint; + +import io.swagger.v3.oas.annotations.Operation; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; +import java.util.List; +import org.apache.solr.client.api.model.ListLevelsResponse; +import org.apache.solr.client.api.model.LogLevelChange; +import org.apache.solr.client.api.model.LogMessagesResponse; +import org.apache.solr.client.api.model.LoggingResponse; +import org.apache.solr.client.api.model.SetThresholdRequestBody; + +@Path("/node/logging") +public interface NodeLoggingApis { + + @GET + @Path("/levels") + @Operation( + summary = "List all log-levels for the target node.", + tags = {"logging"}) + ListLevelsResponse listAllLoggersAndLevels(); + + @PUT + @Path("/levels") + @Operation( + summary = "Set one or more logger levels on the target node.", + tags = {"logging"}) + LoggingResponse modifyLocalLogLevel(List requestBody); + + @GET + @Path("/messages") + @Operation( + summary = "Fetch recent log messages on the targeted node.", + tags = {"logging"}) + LogMessagesResponse fetchLocalLogMessages(@QueryParam("since") Long boundingTimeMillis); + + @PUT + @Path("/messages/threshold") + @Operation( + summary = "Set a threshold level for the targeted node's log message watcher.", + tags = {"logging"}) + LoggingResponse setMessageThreshold(SetThresholdRequestBody requestBody); +} diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/ReplicationApis.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/ReplicationApis.java new file mode 100644 index 00000000000..ac33894b656 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/ReplicationApis.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.endpoint; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; +import java.io.IOException; +import org.apache.solr.client.api.model.FileListResponse; +import org.apache.solr.client.api.model.IndexVersionResponse; +import org.apache.solr.client.api.util.CoreApiParameters; + +@Path("/cores/{coreName}/replication") +public interface ReplicationApis { + + @GET + @CoreApiParameters + @Path("/indexversion") + @Operation( + summary = "Return the index version of the specified core.", + tags = {"replication"}) + IndexVersionResponse fetchIndexVersion() throws IOException; + + @GET + @CoreApiParameters + @Path("/files") + @Operation( + summary = "Return the list of index file that make up the specified core.", + tags = {"replication"}) + FileListResponse fetchFileList( + @Parameter(description = "The generation number of the index", required = true) + @QueryParam("generation") + long gen); +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CategoryRoutedAliasProperties.java b/solr/api/src/java/org/apache/solr/client/api/model/CategoryRoutedAliasProperties.java new file mode 100644 index 00000000000..c3882e0b8a3 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/CategoryRoutedAliasProperties.java @@ -0,0 +1,27 @@ +/* + * 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 CategoryRoutedAliasProperties extends RoutedAliasProperties { + @JsonProperty("maxCardinality") + public Long maxCardinality; + + @JsonProperty("mustMatch") + public String mustMatch; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CreateAliasRequestBody.java b/solr/api/src/java/org/apache/solr/client/api/model/CreateAliasRequestBody.java new file mode 100644 index 00000000000..f4fee3a2d39 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/CreateAliasRequestBody.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.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; + +public class CreateAliasRequestBody { + @JsonProperty(required = true) + public String name; + + @JsonProperty("collections") + public List collections; + + @JsonProperty("async") + public String async; + + @JsonProperty("routers") + public List routers; + + @Schema( + description = + "Parameters to be used for any collections created by this alias. Only used for 'routed' aliases", + name = "collCreationParameters") + @JsonProperty("create-collection") + public CreateCollectionRequestBody collCreationParameters; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/FileListResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/FileListResponse.java new file mode 100644 index 00000000000..d42260449a4 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/FileListResponse.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.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; + +/** Response body for the `GET /api/cores/coreName/replication/files` API */ +public class FileListResponse extends SolrJerseyResponse { + @JsonProperty("filelist") + public List fileList; + + @JsonProperty("confFiles") + public List confFiles; + + @JsonProperty("status") + public String status; + + @JsonProperty("message") + public String message; + + @JsonProperty("exception") + public Exception exception; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/FileMetaData.java b/solr/api/src/java/org/apache/solr/client/api/model/FileMetaData.java new file mode 100644 index 00000000000..79f4d659021 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/FileMetaData.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.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class FileMetaData { + + @JsonProperty("size") + public long size; + + @JsonProperty("name") + public String name; + + @JsonProperty("checksum") + public long checksum; + + @JsonProperty("alias") + public String alias; + + public FileMetaData() {} + + public FileMetaData(long size, String name, long checksum) { + this.size = size; + this.name = name; + this.checksum = checksum; + } +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/IndexVersionResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/IndexVersionResponse.java new file mode 100644 index 00000000000..0d5633ca6e8 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/IndexVersionResponse.java @@ -0,0 +1,40 @@ +/* + * 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; + +/** Response body for the `GET /api/cores/coreName/replication/indexversion` API */ +public class IndexVersionResponse extends SolrJerseyResponse { + + @JsonProperty("indexversion") + public Long indexVersion; + + @JsonProperty("generation") + public Long generation; + + @JsonProperty("status") + public String status; + + public IndexVersionResponse() {} + + public IndexVersionResponse(Long indexVersion, Long generation, String status) { + this.indexVersion = indexVersion; + this.generation = generation; + this.status = status; + } +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/ListLevelsResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/ListLevelsResponse.java new file mode 100644 index 00000000000..b7ad63ac4c1 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/ListLevelsResponse.java @@ -0,0 +1,26 @@ +/* + * 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; + +/** Response format for the 'GET /api/node/logging/levels' API. */ +public class ListLevelsResponse extends LoggingResponse { + @JsonProperty public List levels; + @JsonProperty public List loggers; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/LogLevelChange.java b/solr/api/src/java/org/apache/solr/client/api/model/LogLevelChange.java new file mode 100644 index 00000000000..31443a5dc8d --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/LogLevelChange.java @@ -0,0 +1,32 @@ +/* + * 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; + +/** A user-requested modification in the level that a specified logger reports at. */ +public class LogLevelChange { + public LogLevelChange() {} + + public LogLevelChange(String logger, String level) { + this.logger = logger; + this.level = level; + } + + @JsonProperty public String logger; + @JsonProperty public String level; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/LogLevelInfo.java b/solr/api/src/java/org/apache/solr/client/api/model/LogLevelInfo.java new file mode 100644 index 00000000000..9bde4b1657a --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/LogLevelInfo.java @@ -0,0 +1,39 @@ +/* + * 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; + +/** Representation of a single logger and its current state. */ +public class LogLevelInfo { + public LogLevelInfo() {} + + public LogLevelInfo(String name, String level, boolean set) { + this.name = name; + this.level = level; + this.set = set; + } + + @JsonProperty("name") + public String name; + + @JsonProperty("level") + public String level; + + @JsonProperty("set") + public boolean set; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/LogMessageInfo.java b/solr/api/src/java/org/apache/solr/client/api/model/LogMessageInfo.java new file mode 100644 index 00000000000..7596cc28f68 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/LogMessageInfo.java @@ -0,0 +1,35 @@ +/* + * 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; + +/** Metadata about the log messages returned by the 'GET /api/node/logging/messages' API */ +public class LogMessageInfo { + @JsonProperty("since") + public Long boundingTimeMillis; + + @JsonProperty public Boolean found; + @JsonProperty public List levels; + + @JsonProperty("last") + public long lastRecordTimestampMillis; + + @JsonProperty public int buffer; + @JsonProperty public String threshold; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/LogMessagesResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/LogMessagesResponse.java new file mode 100644 index 00000000000..fb979afdfeb --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/LogMessagesResponse.java @@ -0,0 +1,29 @@ +/* + * 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; + +/** Response format for the 'GET /api/node/logging/messages' API. */ +public class LogMessagesResponse extends LoggingResponse { + @JsonProperty public LogMessageInfo info; + + // TODO Make this declaration more specific. Value on the server side is currently a + // SolrDocumentList, which cannot live in 'api' + @JsonProperty("history") + public Object docs; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/LoggingResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/LoggingResponse.java new file mode 100644 index 00000000000..1ba4be2c0d3 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/LoggingResponse.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; + +/** Generic logging response that includes the name of the log watcher (e.g. "Log4j2") */ +public class LoggingResponse extends SolrJerseyResponse { + @JsonProperty("watcher") + public String watcherName; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/RoutedAliasProperties.java b/solr/api/src/java/org/apache/solr/client/api/model/RoutedAliasProperties.java new file mode 100644 index 00000000000..8355937e70b --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/RoutedAliasProperties.java @@ -0,0 +1,31 @@ +/* + * 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 com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = TimeRoutedAliasProperties.class, name = "time"), + @JsonSubTypes.Type(value = CategoryRoutedAliasProperties.class, name = "category") +}) +public abstract class RoutedAliasProperties { + @JsonProperty(required = true) + public String field; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/SchemaGetDynamicFieldInfoResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/SchemaGetDynamicFieldInfoResponse.java new file mode 100644 index 00000000000..626828c42cd --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/SchemaGetDynamicFieldInfoResponse.java @@ -0,0 +1,27 @@ +/* + * 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 SchemaGetDynamicFieldInfoResponse extends SolrJerseyResponse { + + // TODO Server code sets this field as 'SimpleOrderedMap'; make this type declaration more + // specific once SOLR-12959 is completed + @JsonProperty("dynamicField") + public Object dynamicFieldInfo; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/SchemaGetFieldInfoResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/SchemaGetFieldInfoResponse.java new file mode 100644 index 00000000000..9b94dbda428 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/SchemaGetFieldInfoResponse.java @@ -0,0 +1,27 @@ +/* + * 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 SchemaGetFieldInfoResponse extends SolrJerseyResponse { + + // TODO Server code sets this field as 'SimpleOrderedMap'; make this type declaration more + // specific once SOLR-12959 is completed + @JsonProperty("field") + public Object fieldInfo; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/SchemaGetFieldTypeInfoResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/SchemaGetFieldTypeInfoResponse.java new file mode 100644 index 00000000000..8e243cc111c --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/SchemaGetFieldTypeInfoResponse.java @@ -0,0 +1,27 @@ +/* + * 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 SchemaGetFieldTypeInfoResponse extends SolrJerseyResponse { + + // TODO Server code sets this field as 'SimpleOrderedMap'; make this type declaration more + // specific once SOLR-12959 is completed + @JsonProperty("fieldType") + public Object fieldTypeInfo; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/SchemaListCopyFieldsResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/SchemaListCopyFieldsResponse.java new file mode 100644 index 00000000000..ca18e9fa711 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/SchemaListCopyFieldsResponse.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; +import java.util.List; + +public class SchemaListCopyFieldsResponse extends SolrJerseyResponse { + @JsonProperty("copyFields") + public List copyFields; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/SchemaListDynamicFieldsResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/SchemaListDynamicFieldsResponse.java new file mode 100644 index 00000000000..7c43baf6020 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/SchemaListDynamicFieldsResponse.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; +import java.util.List; + +public class SchemaListDynamicFieldsResponse extends SolrJerseyResponse { + @JsonProperty("dynamicFields") + public List dynamicFields; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/SchemaListFieldTypesResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/SchemaListFieldTypesResponse.java new file mode 100644 index 00000000000..1b8033352a5 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/SchemaListFieldTypesResponse.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; +import java.util.List; + +public class SchemaListFieldTypesResponse extends SolrJerseyResponse { + @JsonProperty("fieldTypes") + public List fieldTypes; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/SchemaListFieldsResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/SchemaListFieldsResponse.java new file mode 100644 index 00000000000..4ab6f737c2d --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/SchemaListFieldsResponse.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; +import java.util.List; + +public class SchemaListFieldsResponse extends SolrJerseyResponse { + @JsonProperty("fields") + public List fields; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/SetThresholdRequestBody.java b/solr/api/src/java/org/apache/solr/client/api/model/SetThresholdRequestBody.java new file mode 100644 index 00000000000..3dd5b070798 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/SetThresholdRequestBody.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** The request body for the 'PUT /api/node/logging/messages/threshold' API. */ +public class SetThresholdRequestBody { + public SetThresholdRequestBody() {} + + public SetThresholdRequestBody(String level) { + this.level = level; + } + + @JsonProperty(required = true) + public String level; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/TimeRoutedAliasProperties.java b/solr/api/src/java/org/apache/solr/client/api/model/TimeRoutedAliasProperties.java new file mode 100644 index 00000000000..8a953cfaf94 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/TimeRoutedAliasProperties.java @@ -0,0 +1,41 @@ +/* + * 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 TimeRoutedAliasProperties extends RoutedAliasProperties { + // Expected to be a date/time in ISO format, or 'NOW' + @JsonProperty(required = true) + public String start; + + // TODO Change this to 'timezone' or something less abbreviated + @JsonProperty("tz") + public String tz; + + @JsonProperty(required = true) + public String interval; + + @JsonProperty("maxFutureMs") + public Long maxFutureMs; + + @JsonProperty("preemptiveCreateMath") + public String preemptiveCreateMath; + + @JsonProperty("autoDeleteAge") + public String autoDeleteAge; +} diff --git a/solr/api/src/java/org/apache/solr/client/api/util/Constants.java b/solr/api/src/java/org/apache/solr/client/api/util/Constants.java index b4ef56c2050..49e69d37fac 100644 --- a/solr/api/src/java/org/apache/solr/client/api/util/Constants.java +++ b/solr/api/src/java/org/apache/solr/client/api/util/Constants.java @@ -27,6 +27,8 @@ private Constants() { public static final String INDEX_PATH_PREFIX = "/{" + INDEX_TYPE_PATH_PARAMETER + ":cores|collections}/{" + INDEX_NAME_PATH_PARAMETER + "}"; + public static final String CORE_NAME_PATH_PARAMETER = "coreName"; + public static final String OMIT_FROM_CODEGEN_PROPERTY = "omitFromCodegen"; public static final String GENERIC_ENTITY_PROPERTY = "genericEntity"; diff --git a/solr/api/src/java/org/apache/solr/client/api/util/CoreApiParameters.java b/solr/api/src/java/org/apache/solr/client/api/util/CoreApiParameters.java new file mode 100644 index 00000000000..7151ee9eda7 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/util/CoreApiParameters.java @@ -0,0 +1,40 @@ +/* + * 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.util; + +import static org.apache.solr.client.api.util.Constants.CORE_NAME_PATH_PARAMETER; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Concisely collects the parameters shared by APIs that interact with contents of a specific core. + * + *

Not to be used on APIs that apply to both cores AND collections. {@link StoreApiParameters} + * should be used in those cases. + * + *

Used primarily as a way to avoid duplicating these parameter definitions on each relevant + * interface method in {@link org.apache.solr.client.api.endpoint} + */ +@Target({ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Parameter(name = CORE_NAME_PATH_PARAMETER, in = ParameterIn.PATH) +public @interface CoreApiParameters {} diff --git a/solr/api/src/java/org/apache/solr/client/api/util/StoreApiParameters.java b/solr/api/src/java/org/apache/solr/client/api/util/StoreApiParameters.java index b82f4cd107a..1d9a66bfa9c 100644 --- a/solr/api/src/java/org/apache/solr/client/api/util/StoreApiParameters.java +++ b/solr/api/src/java/org/apache/solr/client/api/util/StoreApiParameters.java @@ -29,8 +29,10 @@ import org.apache.solr.client.api.model.IndexType; /** - * Concisely collects the parameters shared by APIs that interact with contents of a specific - * core/collection. + * Concisely collects the parameters shared by APIs that interact with contents of a specific core + * OR collection. + * + *

Not to be used on APIs that are only available on cores or only on collections. * *

Used primarily as a way to avoid duplicating these parameter definitions on each relevant * interface method in {@link org.apache.solr.client.api.endpoint} diff --git a/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java b/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java index b609b5fb749..0f67ea810e5 100644 --- a/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java +++ b/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java @@ -96,6 +96,7 @@ import org.apache.lucene.store.IOContext; import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexOutput; +import org.apache.solr.client.api.model.FileMetaData; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.Http2SolrClient; import org.apache.solr.client.solrj.impl.HttpClientUtil; @@ -122,7 +123,6 @@ import org.apache.solr.core.IndexDeletionPolicyWrapper; import org.apache.solr.core.SolrCore; import org.apache.solr.handler.ReplicationHandler.FileInfo; -import org.apache.solr.handler.admin.api.CoreReplicationAPI; import org.apache.solr.request.LocalSolrQueryRequest; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.search.SolrIndexSearcher; @@ -1600,10 +1600,10 @@ private Collection> getModifiedConfFiles( names.add(name, null); } // get the details of the local conf files with the same alias/name - List localFilesInfo = + List localFilesInfo = replicationHandler.getConfFileInfoFromCache(names, confFileInfoCache); // compare their size/checksum to see if - for (CoreReplicationAPI.FileMetaData fileInfo : localFilesInfo) { + for (FileMetaData fileInfo : localFilesInfo) { String name = fileInfo.name; Map m = nameVsFile.get(name); if (m == null) continue; // the file is not even present locally (so must be downloaded) diff --git a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java index 463fc9a046b..1f43cc3257f 100644 --- a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java @@ -66,6 +66,8 @@ import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.RateLimiter; import org.apache.solr.api.JerseyResource; +import org.apache.solr.client.api.model.FileMetaData; +import org.apache.solr.client.api.model.IndexVersionResponse; import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; @@ -91,11 +93,10 @@ import org.apache.solr.core.backup.repository.LocalFileSystemRepository; import org.apache.solr.handler.IndexFetcher.IndexFetchResult; import org.apache.solr.handler.ReplicationHandler.ReplicationHandlerConfig; -import org.apache.solr.handler.admin.api.CoreReplicationAPI; +import org.apache.solr.handler.admin.api.CoreReplication; import org.apache.solr.handler.admin.api.SnapshotBackupAPI; import org.apache.solr.handler.api.V2ApiUtils; import org.apache.solr.jersey.APIConfigProvider; -import org.apache.solr.jersey.APIConfigProvider.APIConfig; import org.apache.solr.metrics.MetricsMap; import org.apache.solr.metrics.SolrMetricsContext; import org.apache.solr.request.SolrQueryRequest; @@ -274,7 +275,7 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw } else if (command.equals(CMD_GET_FILE)) { getFileStream(solrParams, rsp); } else if (command.equals(CMD_GET_FILE_LIST)) { - final CoreReplicationAPI coreReplicationAPI = new CoreReplicationAPI(core, req, rsp); + final CoreReplication coreReplicationAPI = new CoreReplication(core, req, rsp); V2ApiUtils.squashIntoSolrResponseWithoutHeader( rsp, coreReplicationAPI.fetchFileList(Long.parseLong(solrParams.required().get(GENERATION)))); @@ -696,10 +697,10 @@ private void getFileStream(SolrParams solrParams, SolrQueryResponse rsp) { rsp.add(STATUS, OK_STATUS); } - public CoreReplicationAPI.IndexVersionResponse getIndexVersionResponse() throws IOException { + public IndexVersionResponse getIndexVersionResponse() throws IOException { IndexCommit commitPoint = indexCommitPoint; // make a copy so it won't change - CoreReplicationAPI.IndexVersionResponse rsp = new CoreReplicationAPI.IndexVersionResponse(); + IndexVersionResponse rsp = new IndexVersionResponse(); if (commitPoint == null) { // if this handler is 'lazy', we may not have tracked the last commit // because our commit listener is registered on inform @@ -735,9 +736,9 @@ public CoreReplicationAPI.IndexVersionResponse getIndexVersionResponse() throws *

The local conf files information is cached so that everytime it does not have to compute the * checksum. The cache is refreshed only if the lastModified of the file changes */ - public List getConfFileInfoFromCache( + public List getConfFileInfoFromCache( NamedList nameAndAlias, final Map confFileInfoCache) { - List confFiles = new ArrayList<>(); + List confFiles = new ArrayList<>(); synchronized (confFileInfoCache) { Checksum checksum = null; for (int i = 0; i < nameAndAlias.size(); i++) { @@ -758,7 +759,7 @@ public List getConfFileInfoFromCache( info = new FileInfo(lastModified, cf, size, getCheckSum(checksum, f)); confFileInfoCache.put(cf, info); } - CoreReplicationAPI.FileMetaData m = info.fileMetaData; + FileMetaData m = info.fileMetaData; if (nameAndAlias.getVal(i) != null) m.alias = nameAndAlias.getVal(i); confFiles.add(m); } @@ -768,11 +769,11 @@ public List getConfFileInfoFromCache( static class FileInfo { long lastmodified; - CoreReplicationAPI.FileMetaData fileMetaData; + FileMetaData fileMetaData; public FileInfo(long lasmodified, String name, long size, long checksum) { this.lastmodified = lasmodified; - this.fileMetaData = new CoreReplicationAPI.FileMetaData(size, name, checksum); + this.fileMetaData = new FileMetaData(size, name, checksum); } } @@ -1389,7 +1390,7 @@ public void inform(SolrCore core) { @Override public Collection> getJerseyResources() { - return List.of(CoreReplicationAPI.class, SnapshotBackupAPI.class); + return List.of(CoreReplication.class, SnapshotBackupAPI.class); } @Override @@ -1913,7 +1914,7 @@ private static Long readIntervalNs(String interval) { */ public static final String WAIT = "wait"; - public static class ReplicationHandlerConfig implements APIConfig { + public static class ReplicationHandlerConfig implements APIConfigProvider.APIConfig { private int numberBackupsToKeep = 0; // zero: do not delete old backups diff --git a/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java b/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java index 6d8e7c319bf..c81fa8eb572 100644 --- a/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java @@ -42,7 +42,7 @@ import org.apache.solr.common.util.StrUtils; import org.apache.solr.core.SolrCore; import org.apache.solr.handler.admin.api.GetSchema; -import org.apache.solr.handler.admin.api.GetSchemaFieldAPI; +import org.apache.solr.handler.admin.api.GetSchemaField; import org.apache.solr.handler.admin.api.SchemaBulkModifyAPI; import org.apache.solr.handler.api.V2ApiUtils; import org.apache.solr.request.SolrQueryRequest; @@ -178,12 +178,12 @@ private void handleGET(SolrQueryRequest req, SolrQueryResponse rsp) { if (parts.size() > 2) { V2ApiUtils.squashIntoSolrResponseWithoutHeader( rsp, - new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + new GetSchemaField(req.getCore().getLatestSchema(), req.getParams()) .getFieldInfo(parts.get(2))); } else { V2ApiUtils.squashIntoSolrResponseWithoutHeader( rsp, - new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + new GetSchemaField(req.getCore().getLatestSchema(), req.getParams()) .listSchemaFields()); } return; @@ -192,7 +192,7 @@ private void handleGET(SolrQueryRequest req, SolrQueryResponse rsp) { { V2ApiUtils.squashIntoSolrResponseWithoutHeader( rsp, - new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + new GetSchemaField(req.getCore().getLatestSchema(), req.getParams()) .listCopyFields()); return; } @@ -201,12 +201,12 @@ private void handleGET(SolrQueryRequest req, SolrQueryResponse rsp) { if (parts.size() > 2) { V2ApiUtils.squashIntoSolrResponseWithoutHeader( rsp, - new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + new GetSchemaField(req.getCore().getLatestSchema(), req.getParams()) .getDynamicFieldInfo(parts.get(2))); } else { V2ApiUtils.squashIntoSolrResponseWithoutHeader( rsp, - new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + new GetSchemaField(req.getCore().getLatestSchema(), req.getParams()) .listDynamicFields()); } return; @@ -216,12 +216,12 @@ private void handleGET(SolrQueryRequest req, SolrQueryResponse rsp) { if (parts.size() > 2) { V2ApiUtils.squashIntoSolrResponseWithoutHeader( rsp, - new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + new GetSchemaField(req.getCore().getLatestSchema(), req.getParams()) .getFieldTypeInfo(parts.get(2))); } else { V2ApiUtils.squashIntoSolrResponseWithoutHeader( rsp, - new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + new GetSchemaField(req.getCore().getLatestSchema(), req.getParams()) .listSchemaFieldTypes()); } return; @@ -294,7 +294,7 @@ public Collection getApis() { @Override public Collection> getJerseyResources() { - return List.of(GetSchema.class, GetSchemaFieldAPI.class); + return List.of(GetSchema.class, GetSchemaField.class); } @Override 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 996b4e3c4ce..42fd24261ce 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 @@ -122,6 +122,7 @@ 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.CreateAliasRequestBody; import org.apache.solr.client.api.model.CreateCollectionSnapshotRequestBody; import org.apache.solr.client.api.model.CreateCollectionSnapshotResponse; import org.apache.solr.client.api.model.InstallShardDataRequestBody; @@ -174,7 +175,7 @@ import org.apache.solr.handler.admin.api.BalanceShardUnique; import org.apache.solr.handler.admin.api.CollectionProperty; import org.apache.solr.handler.admin.api.CollectionStatusAPI; -import org.apache.solr.handler.admin.api.CreateAliasAPI; +import org.apache.solr.handler.admin.api.CreateAlias; import org.apache.solr.handler.admin.api.CreateCollection; import org.apache.solr.handler.admin.api.CreateCollectionBackup; import org.apache.solr.handler.admin.api.CreateCollectionSnapshot; @@ -618,10 +619,9 @@ public enum CollectionOperation implements CollectionOp { CREATEALIAS_OP( CREATEALIAS, (req, rsp, h) -> { - final CreateAliasAPI.CreateAliasRequestBody reqBody = - CreateAliasAPI.createFromSolrParams(req.getParams()); + final CreateAliasRequestBody reqBody = CreateAlias.createFromSolrParams(req.getParams()); final SolrJerseyResponse response = - new CreateAliasAPI(h.coreContainer, req, rsp).createAlias(reqBody); + new CreateAlias(h.coreContainer, req, rsp).createAlias(reqBody); V2ApiUtils.squashIntoSolrResponseWithoutHeader(rsp, response); return null; }), @@ -1360,7 +1360,7 @@ public Collection> getJerseyResources() { CreateReplica.class, AddReplicaProperty.class, BalanceShardUnique.class, - CreateAliasAPI.class, + CreateAlias.class, CreateCollection.class, CreateCollectionBackup.class, CreateShard.class, diff --git a/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java index 67dbe69cd9c..7593bb7cbdd 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java @@ -22,12 +22,15 @@ import java.util.List; import org.apache.solr.api.Api; import org.apache.solr.api.JerseyResource; +import org.apache.solr.client.api.model.LogLevelChange; +import org.apache.solr.client.api.model.LoggingResponse; +import org.apache.solr.client.api.model.SetThresholdRequestBody; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.params.SolrParams; import org.apache.solr.core.CoreContainer; import org.apache.solr.handler.RequestHandlerBase; -import org.apache.solr.handler.admin.api.NodeLoggingAPI; +import org.apache.solr.handler.admin.api.NodeLogging; import org.apache.solr.handler.api.V2ApiUtils; import org.apache.solr.logging.LogWatcher; import org.apache.solr.request.SolrQueryRequest; @@ -54,25 +57,23 @@ public LoggingHandler(CoreContainer cc) { @Override public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - final NodeLoggingAPI loggingApi = new NodeLoggingAPI(cc); + final NodeLogging loggingApi = new NodeLogging(cc); SolrParams params = req.getParams(); if (params.get("threshold") != null) { squashV2Response( rsp, - loggingApi.setMessageThreshold( - new NodeLoggingAPI.SetThresholdRequestBody(params.get("threshold")))); + loggingApi.setMessageThreshold(new SetThresholdRequestBody(params.get("threshold")))); } // Write something at each level if (params.get("test") != null) { - NodeLoggingAPI.writeLogsForTesting(); + NodeLogging.writeLogsForTesting(); } String[] set = params.getParams("set"); if (set != null) { - final List changes = - NodeLoggingAPI.LogLevelChange.createRequestBodyFromV1Params(set); + final List changes = NodeLogging.parseLogLevelChanges(set); squashV2Response(rsp, loggingApi.modifyLocalLogLevel(changes)); } @@ -95,7 +96,7 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw } } - private void squashV2Response(SolrQueryResponse rsp, NodeLoggingAPI.LoggingResponse response) { + private void squashV2Response(SolrQueryResponse rsp, LoggingResponse response) { V2ApiUtils.squashIntoSolrResponseWithoutHeader(rsp, response); } @@ -118,7 +119,7 @@ public Collection getApis() { @Override public Collection> getJerseyResources() { - return List.of(NodeLoggingAPI.class); + return List.of(NodeLogging.class); } @Override diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/V2NodeLoggingAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplication.java similarity index 51% rename from solr/core/src/java/org/apache/solr/handler/admin/api/V2NodeLoggingAPI.java rename to solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplication.java index a2b97609d0a..c4071ced8fc 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/V2NodeLoggingAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplication.java @@ -14,39 +14,41 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.solr.handler.admin.api; -import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM; +import static org.apache.solr.security.PermissionNameProvider.Name.CORE_READ_PERM; import jakarta.inject.Inject; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; -import java.util.List; +import java.io.IOException; +import org.apache.solr.client.api.endpoint.ReplicationApis; +import org.apache.solr.client.api.model.FileListResponse; +import org.apache.solr.client.api.model.IndexVersionResponse; +import org.apache.solr.core.SolrCore; import org.apache.solr.jersey.PermissionName; -import org.apache.solr.logging.LogWatcher; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; /** - * V2 API for getting log levels on an individual node. + * V2 API implementation of {@link ReplicationApis} * - *

The endpoint is defined as api/node/logging/levels + *

These APIs are analogous to the v1 /coreName/replication APIs. */ -@Path("/api/node/logging") -public class V2NodeLoggingAPI { - private final LogWatcher watcher; +public class CoreReplication extends ReplicationAPIBase implements ReplicationApis { @Inject - public V2NodeLoggingAPI(LogWatcher watcher) { - this.watcher = watcher; + public CoreReplication(SolrCore solrCore, SolrQueryRequest req, SolrQueryResponse rsp) { + super(solrCore, req, rsp); + } + + @Override + @PermissionName(CORE_READ_PERM) + public IndexVersionResponse fetchIndexVersion() throws IOException { + return doFetchIndexVersion(); } - @GET - @Path("/levels") - @PermissionName(CONFIG_EDIT_PERM) - @Produces(MediaType.APPLICATION_JSON) - public List getAllLevels() { - return this.watcher.getAllLevels(); + @Override + @PermissionName(CORE_READ_PERM) + public FileListResponse fetchFileList(long gen) { + return doFetchFileList(gen); } } diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplicationAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplicationAPI.java deleted file mode 100644 index f6bf4f4c05d..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplicationAPI.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.solr.handler.admin.api; - -import static org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2; -import static org.apache.solr.security.PermissionNameProvider.Name.CORE_READ_PERM; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.Parameter; -import jakarta.inject.Inject; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.MediaType; -import java.io.IOException; -import java.util.List; -import org.apache.solr.client.api.model.SolrJerseyResponse; -import org.apache.solr.core.SolrCore; -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 APIs for inspecting and replicating indices - * - *

These APIs are analogous to the v1 /coreName/replication APIs. - */ -@Path("/cores/{coreName}/replication") -public class CoreReplicationAPI extends ReplicationAPIBase { - - @Inject - public CoreReplicationAPI(SolrCore solrCore, SolrQueryRequest req, SolrQueryResponse rsp) { - super(solrCore, req, rsp); - } - - @GET - @Path("/indexversion") - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2}) - @PermissionName(CORE_READ_PERM) - public IndexVersionResponse fetchIndexVersion() throws IOException { - return doFetchIndexVersion(); - } - - @GET - @Path("/files") - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2}) - @PermissionName(CORE_READ_PERM) - public FileListResponse fetchFileList( - @Parameter(description = "The generation number of the index", required = true) - @QueryParam("generation") - long gen) { - return doFetchFileList(gen); - } - - /** Response for {@link CoreReplicationAPI#fetchIndexVersion()}. */ - public static class IndexVersionResponse extends SolrJerseyResponse { - - @JsonProperty("indexversion") - public Long indexVersion; - - @JsonProperty("generation") - public Long generation; - - @JsonProperty("status") - public String status; - - public IndexVersionResponse() {} - - public IndexVersionResponse(Long indexVersion, Long generation, String status) { - this.indexVersion = indexVersion; - this.generation = generation; - this.status = status; - } - } - - /** Response for {@link CoreReplicationAPI#fetchFileList(long)}. */ - public static class FileListResponse extends SolrJerseyResponse { - @JsonProperty("filelist") - public List fileList; - - @JsonProperty("confFiles") - public List confFiles; - - @JsonProperty("status") - public String status; - - @JsonProperty("message") - public String message; - - @JsonProperty("exception") - public Exception exception; - - public FileListResponse() {} - } - - /** - * Contained in {@link CoreReplicationAPI.FileListResponse}, this holds metadata from a file for - * an index - */ - public static class FileMetaData implements JacksonReflectMapWriter { - - @JsonProperty("size") - public long size; - - @JsonProperty("name") - public String name; - - @JsonProperty("checksum") - public long checksum; - - @JsonProperty("alias") - public String alias; - - public FileMetaData() {} - - public FileMetaData(long size, String name, long checksum) { - this.size = size; - this.name = name; - this.checksum = checksum; - } - } -} 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/CreateAlias.java similarity index 69% rename from solr/core/src/java/org/apache/solr/handler/admin/api/CreateAliasAPI.java rename to solr/core/src/java/org/apache/solr/handler/admin/api/CreateAlias.java index 8e8ecaf405f..c0497891b78 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/CreateAlias.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.COLLECTIONS; import static org.apache.solr.cloud.Overseer.QUEUE_OPERATION; import static org.apache.solr.cloud.api.collections.RoutedAlias.CATEGORY; @@ -26,6 +25,7 @@ import static org.apache.solr.cloud.api.collections.RoutedAlias.TIME; import static org.apache.solr.cloud.api.collections.TimeRoutedAlias.ROUTER_MAX_FUTURE; import static org.apache.solr.common.SolrException.ErrorCode.BAD_REQUEST; +import static org.apache.solr.common.SolrException.ErrorCode.SERVER_ERROR; import static org.apache.solr.common.params.CollectionAdminParams.ROUTER_PREFIX; import static org.apache.solr.common.params.CommonAdminParams.ASYNC; import static org.apache.solr.common.params.CommonParams.NAME; @@ -33,23 +33,19 @@ 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 com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; import jakarta.inject.Inject; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Locale; import java.util.Map; -import org.apache.solr.client.api.model.CreateCollectionRequestBody; +import org.apache.solr.client.api.endpoint.CreateAliasApi; +import org.apache.solr.client.api.model.CategoryRoutedAliasProperties; +import org.apache.solr.client.api.model.CreateAliasRequestBody; +import org.apache.solr.client.api.model.RoutedAliasProperties; import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse; +import org.apache.solr.client.api.model.TimeRoutedAliasProperties; import org.apache.solr.client.solrj.RoutedAliasTypes; import org.apache.solr.client.solrj.SolrResponse; import org.apache.solr.client.solrj.util.SolrIdentifierValidator; @@ -66,7 +62,6 @@ import org.apache.solr.common.util.StrUtils; 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; @@ -77,18 +72,16 @@ * *

This API is analogous to the v1 /admin/collections?action=CREATEALIAS command. */ -@Path("/aliases") -public class CreateAliasAPI extends AdminAPIBase { +public class CreateAlias extends AdminAPIBase implements CreateAliasApi { @Inject - public CreateAliasAPI( + public CreateAlias( 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 SolrJerseyResponse createAlias(CreateAliasRequestBody requestBody) throws Exception { final SubResponseAccumulatingJerseyResponse response = @@ -98,7 +91,7 @@ public SolrJerseyResponse createAlias(CreateAliasRequestBody requestBody) throws if (requestBody == null) { throw new SolrException(BAD_REQUEST, "Request body is required but missing"); } - requestBody.validate(); + validateRequestBody(requestBody); ZkNodeProps remoteMessage; // Validation ensures that the request has either collections or a router but not both. @@ -153,10 +146,12 @@ public static ZkNodeProps createRemoteMessageForRoutedAlias(CreateAliasRequestBo if (requestBody.routers.size() > 1) { // Multi-dimensional alias for (int i = 0; i < requestBody.routers.size(); i++) { - requestBody.routers.get(i).addRemoteMessageProperties(remoteMessage, "router." + i + "."); + createValidationHelper(requestBody.routers.get(i)) + .addRemoteMessageProperties(remoteMessage, "router." + i + "."); } } else if (requestBody.routers.size() == 1) { // Single dimensional alias - requestBody.routers.get(0).addRemoteMessageProperties(remoteMessage, "router."); + createValidationHelper(requestBody.routers.get(0)) + .addRemoteMessageProperties(remoteMessage, "router."); } if (requestBody.collCreationParameters != null) { @@ -213,9 +208,9 @@ public static RoutedAliasProperties createFromSolrParams( String type, SolrParams params, String propertyPrefix) { final String typeLower = type.toLowerCase(Locale.ROOT); if (typeLower.startsWith(TIME)) { - return TimeRoutedAliasProperties.createFromSolrParams(params, propertyPrefix); + return TimeRoutedAliasValidationHelper.createFromSolrParams(params, propertyPrefix); } else if (typeLower.startsWith(CATEGORY)) { - return CategoryRoutedAliasProperties.createFromSolrParams(params, propertyPrefix); + return CategoryRoutedAliasValidationHelper.createFromSolrParams(params, propertyPrefix); } else { throw new SolrException( BAD_REQUEST, @@ -226,71 +221,46 @@ public static RoutedAliasProperties createFromSolrParams( } } - public static class CreateAliasRequestBody implements JacksonReflectMapWriter { - @JsonProperty(required = true) - public String name; + public static void validateRequestBody(CreateAliasRequestBody requestBody) { + SolrIdentifierValidator.validateAliasName(requestBody.name); - @JsonProperty("collections") - public List collections; - - @JsonProperty(ASYNC) - public String async; - - @JsonProperty("routers") - public List routers; - - @JsonProperty("create-collection") - public CreateCollectionRequestBody collCreationParameters; - - public void validate() { - SolrIdentifierValidator.validateAliasName(name); + if (CollectionUtil.isEmpty(requestBody.collections) + && CollectionUtil.isEmpty(requestBody.routers)) { + throw new SolrException( + BAD_REQUEST, + "Alias creation requires either a list of either collections (for creating a traditional alias) or routers (for creating a routed alias)"); + } - if (CollectionUtil.isEmpty(collections) && CollectionUtil.isEmpty(routers)) { + if (CollectionUtil.isNotEmpty(requestBody.routers)) { + requestBody.routers.forEach(r -> createValidationHelper(r).validate()); + if (CollectionUtil.isNotEmpty(requestBody.collections)) { throw new SolrException( - BAD_REQUEST, - "Alias creation requires either a list of either collections (for creating a traditional alias) or routers (for creating a routed alias)"); + BAD_REQUEST, "Collections cannot be specified when creating a routed alias."); } - if (CollectionUtil.isNotEmpty(routers)) { - routers.forEach(r -> r.validate()); - if (CollectionUtil.isNotEmpty(collections)) { + final var createCollReqBody = requestBody.collCreationParameters; + if (createCollReqBody != null) { + if (createCollReqBody.name != null) { throw new SolrException( - BAD_REQUEST, "Collections cannot be specified when creating a routed alias."); + BAD_REQUEST, + "routed aliases calculate names for their " + + "dependent collections, you cannot specify the name."); } - - final var createCollReqBody = collCreationParameters; - if (createCollReqBody != null) { - if (createCollReqBody.name != null) { - throw new SolrException( - BAD_REQUEST, - "routed aliases calculate names for their " - + "dependent collections, you cannot specify the name."); - } - if (createCollReqBody.config == null) { - throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, - "Routed alias creation requires a configset name to use for any collections created by the alias."); - } + if (createCollReqBody.config == null) { + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, + "Routed alias creation requires a configset name to use for any collections created by the alias."); } } } } - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(value = TimeRoutedAliasProperties.class, name = "time"), - @JsonSubTypes.Type(value = CategoryRoutedAliasProperties.class, name = "category") - }) - public abstract static class RoutedAliasProperties implements JacksonReflectMapWriter { - @JsonProperty(required = true) - public String field; + private interface RoutedAliasValidationHelper { + void validate(); - public abstract void validate(); + void addRemoteMessageProperties(Map remoteMessage, String prefix); - public abstract void addRemoteMessageProperties( - Map remoteMessage, String prefix); - - protected void ensureRequiredFieldPresent(Object val, String name) { + default void ensureRequiredFieldPresent(Object val, String name) { if (val == null) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "Missing required parameter: " + name); @@ -298,38 +268,38 @@ protected void ensureRequiredFieldPresent(Object val, String name) { } } - public static class TimeRoutedAliasProperties extends RoutedAliasProperties { - // Expected to be a date/time in ISO format, or 'NOW' - @JsonProperty(required = true) - public String start; - - // TODO Change this to 'timezone' or something less abbreviated - @JsonProperty("tz") - public String tz; - - @JsonProperty(required = true) - public String interval; - - @JsonProperty("maxFutureMs") - public Long maxFutureMs; + private static RoutedAliasValidationHelper createValidationHelper( + RoutedAliasProperties routedAliasProperties) { + if (routedAliasProperties instanceof TimeRoutedAliasProperties) { + return new TimeRoutedAliasValidationHelper((TimeRoutedAliasProperties) routedAliasProperties); + } else if (routedAliasProperties instanceof CategoryRoutedAliasProperties) { + return new CategoryRoutedAliasValidationHelper( + (CategoryRoutedAliasProperties) routedAliasProperties); + } else { + throw new SolrException( + SERVER_ERROR, "Unrecognized routed-alias type provided: " + routedAliasProperties); + } + } - @JsonProperty("preemptiveCreateMath") - public String preemptiveCreateMath; + public static class TimeRoutedAliasValidationHelper implements RoutedAliasValidationHelper { + private final TimeRoutedAliasProperties aliasProperties; - @JsonProperty("autoDeleteAge") - public String autoDeleteAge; + public TimeRoutedAliasValidationHelper(TimeRoutedAliasProperties aliasProperties) { + this.aliasProperties = aliasProperties; + } @Override public void validate() { - ensureRequiredFieldPresent(field, "'field' on time routed alias"); - ensureRequiredFieldPresent(start, "'start' on time routed alias"); - ensureRequiredFieldPresent(interval, "'interval' on time routed alias"); + ensureRequiredFieldPresent(aliasProperties.field, "'field' on time routed alias"); + ensureRequiredFieldPresent(aliasProperties.start, "'start' on time routed alias"); + ensureRequiredFieldPresent(aliasProperties.interval, "'interval' on time routed alias"); // Ensures that provided 'start' and optional 'tz' are of the right format. - TimeRoutedAlias.parseStringAsInstant(start, TimeZoneUtils.parseTimezone(tz)); + TimeRoutedAlias.parseStringAsInstant( + aliasProperties.start, TimeZoneUtils.parseTimezone(aliasProperties.tz)); // maxFutureMs must be > 0 if provided - if (maxFutureMs != null && maxFutureMs < 0) { + if (aliasProperties.maxFutureMs != null && aliasProperties.maxFutureMs < 0) { throw new SolrException(BAD_REQUEST, ROUTER_MAX_FUTURE + " must be >= 0"); } } @@ -337,15 +307,17 @@ public void validate() { @Override public void addRemoteMessageProperties(Map remoteMessage, String prefix) { remoteMessage.put(prefix + CoreAdminParams.NAME, TIME); - remoteMessage.put(prefix + "field", field); - remoteMessage.put(prefix + "start", start); - remoteMessage.put(prefix + "interval", interval); - - if (tz != null) remoteMessage.put(prefix + "tz", tz); - if (maxFutureMs != null) remoteMessage.put(prefix + "maxFutureMs", maxFutureMs); - if (preemptiveCreateMath != null) - remoteMessage.put(prefix + "preemptiveCreateMath", preemptiveCreateMath); - if (autoDeleteAge != null) remoteMessage.put(prefix + "autoDeleteAge", autoDeleteAge); + remoteMessage.put(prefix + "field", aliasProperties.field); + remoteMessage.put(prefix + "start", aliasProperties.start); + remoteMessage.put(prefix + "interval", aliasProperties.interval); + + if (aliasProperties.tz != null) remoteMessage.put(prefix + "tz", aliasProperties.tz); + if (aliasProperties.maxFutureMs != null) + remoteMessage.put(prefix + "maxFutureMs", aliasProperties.maxFutureMs); + if (aliasProperties.preemptiveCreateMath != null) + remoteMessage.put(prefix + "preemptiveCreateMath", aliasProperties.preemptiveCreateMath); + if (aliasProperties.autoDeleteAge != null) + remoteMessage.put(prefix + "autoDeleteAge", aliasProperties.autoDeleteAge); } public static TimeRoutedAliasProperties createFromSolrParams( @@ -365,25 +337,27 @@ public static TimeRoutedAliasProperties createFromSolrParams( } } - public static class CategoryRoutedAliasProperties extends RoutedAliasProperties { - @JsonProperty("maxCardinality") - public Long maxCardinality; + public static class CategoryRoutedAliasValidationHelper implements RoutedAliasValidationHelper { + private final CategoryRoutedAliasProperties aliasProperties; - @JsonProperty("mustMatch") - public String mustMatch; + public CategoryRoutedAliasValidationHelper(CategoryRoutedAliasProperties aliasProperties) { + this.aliasProperties = aliasProperties; + } @Override public void validate() { - ensureRequiredFieldPresent(field, "'field' on category routed alias"); + ensureRequiredFieldPresent(aliasProperties.field, "'field' on category routed alias"); } @Override public void addRemoteMessageProperties(Map remoteMessage, String prefix) { remoteMessage.put(prefix + CoreAdminParams.NAME, CATEGORY); - remoteMessage.put(prefix + "field", field); + remoteMessage.put(prefix + "field", aliasProperties.field); - if (maxCardinality != null) remoteMessage.put(prefix + "maxCardinality", maxCardinality); - if (StrUtils.isNotBlank(mustMatch)) remoteMessage.put(prefix + "mustMatch", mustMatch); + if (aliasProperties.maxCardinality != null) + remoteMessage.put(prefix + "maxCardinality", aliasProperties.maxCardinality); + if (StrUtils.isNotBlank(aliasProperties.mustMatch)) + remoteMessage.put(prefix + "mustMatch", aliasProperties.mustMatch); } public static CategoryRoutedAliasProperties createFromSolrParams( diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaFieldAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaField.java similarity index 74% rename from solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaFieldAPI.java rename to solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaField.java index 5050787429c..3a82d9d6fa5 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaFieldAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaField.java @@ -17,19 +17,18 @@ package org.apache.solr.handler.admin.api; -import static org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2; - -import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.inject.Inject; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; import java.util.List; import java.util.Map; import org.apache.solr.api.JerseyResource; -import org.apache.solr.client.api.model.SolrJerseyResponse; +import org.apache.solr.client.api.endpoint.GetSchemaApi; +import org.apache.solr.client.api.model.SchemaGetDynamicFieldInfoResponse; +import org.apache.solr.client.api.model.SchemaGetFieldInfoResponse; +import org.apache.solr.client.api.model.SchemaGetFieldTypeInfoResponse; +import org.apache.solr.client.api.model.SchemaListCopyFieldsResponse; +import org.apache.solr.client.api.model.SchemaListDynamicFieldsResponse; +import org.apache.solr.client.api.model.SchemaListFieldTypesResponse; +import org.apache.solr.client.api.model.SchemaListFieldsResponse; import org.apache.solr.common.MapWriter; import org.apache.solr.common.SolrException; import org.apache.solr.common.cloud.SolrClassLoader; @@ -55,8 +54,7 @@ *

  • /fieldtypes/{fieldTypeName} * */ -@Path("/{a:cores|collections}/{collectionName}/schema") -public class GetSchemaFieldAPI /*extends GetSchemaAPI*/ extends JerseyResource { +public class GetSchemaField extends JerseyResource implements GetSchemaApi.Fields { private final IndexSchema indexSchema; private final SolrParams params; @@ -64,14 +62,12 @@ public class GetSchemaFieldAPI /*extends GetSchemaAPI*/ extends JerseyResource { // TODO Stop using SolrParams here and instead give API methods parameters representing only those // query-params that they support @Inject - public GetSchemaFieldAPI(IndexSchema indexSchema, SolrParams params) { + public GetSchemaField(IndexSchema indexSchema, SolrParams params) { this.indexSchema = indexSchema; this.params = params; } - @GET - @Path("/fields") - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2}) + @Override @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) public SchemaListFieldsResponse listSchemaFields() { SchemaListFieldsResponse response = instantiateJerseyResponse(SchemaListFieldsResponse.class); @@ -82,16 +78,9 @@ public SchemaListFieldsResponse listSchemaFields() { return response; } - public static class SchemaListFieldsResponse extends SolrJerseyResponse { - @JsonProperty("fields") - public List fields; - } - - @GET - @Path("/fields/{fieldName}") - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_ATOM_XML, BINARY_CONTENT_TYPE_V2}) + @Override @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) - public SchemaGetFieldInfoResponse getFieldInfo(@PathParam("fieldName") String fieldName) { + public SchemaGetFieldInfoResponse getFieldInfo(String fieldName) { if (fieldName == null) { throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Field name must not be null"); } @@ -107,14 +96,7 @@ public SchemaGetFieldInfoResponse getFieldInfo(@PathParam("fieldName") String fi throw new SolrException(SolrException.ErrorCode.NOT_FOUND, "No such field [" + fieldName + "]"); } - public static class SchemaGetFieldInfoResponse extends SolrJerseyResponse { - @JsonProperty("field") - public SimpleOrderedMap fieldInfo; - } - - @GET - @Path("/copyfields") - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2}) + @Override @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) public SchemaListCopyFieldsResponse listCopyFields() { SchemaListCopyFieldsResponse response = @@ -126,14 +108,7 @@ public SchemaListCopyFieldsResponse listCopyFields() { return response; } - public static class SchemaListCopyFieldsResponse extends SolrJerseyResponse { - @JsonProperty("copyFields") - public List copyFields; - } - - @GET - @Path("/dynamicfields") - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2}) + @Override @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) public SchemaListDynamicFieldsResponse listDynamicFields() { SchemaListDynamicFieldsResponse response = @@ -145,17 +120,9 @@ public SchemaListDynamicFieldsResponse listDynamicFields() { return response; } - public static class SchemaListDynamicFieldsResponse extends SolrJerseyResponse { - @JsonProperty("dynamicFields") - public List dynamicFields; - } - - @GET - @Path("/dynamicfields/{fieldName}") - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_ATOM_XML, BINARY_CONTENT_TYPE_V2}) + @Override @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) - public SchemaGetDynamicFieldInfoResponse getDynamicFieldInfo( - @PathParam("fieldName") String fieldName) { + public SchemaGetDynamicFieldInfoResponse getDynamicFieldInfo(String fieldName) { if (fieldName == null) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "Dynamic field name must not be null"); @@ -174,14 +141,7 @@ public SchemaGetDynamicFieldInfoResponse getDynamicFieldInfo( SolrException.ErrorCode.NOT_FOUND, "No such dynamic field [" + fieldName + "]"); } - public static class SchemaGetDynamicFieldInfoResponse extends SolrJerseyResponse { - @JsonProperty("dynamicField") - public SimpleOrderedMap dynamicFieldInfo; - } - - @GET - @Path("/fieldtypes") - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2}) + @Override @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) public SchemaListFieldTypesResponse listSchemaFieldTypes() { SchemaListFieldTypesResponse response = @@ -193,17 +153,9 @@ public SchemaListFieldTypesResponse listSchemaFieldTypes() { return response; } - public static class SchemaListFieldTypesResponse extends SolrJerseyResponse { - @JsonProperty("fieldTypes") - public List fieldTypes; - } - - @GET - @Path("/fieldtypes/{fieldTypeName}") - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_ATOM_XML, BINARY_CONTENT_TYPE_V2}) + @Override @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) - public SchemaGetFieldTypeInfoResponse getFieldTypeInfo( - @PathParam("fieldTypeName") String fieldTypeName) { + public SchemaGetFieldTypeInfoResponse getFieldTypeInfo(String fieldTypeName) { if (fieldTypeName == null) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "Field type name must not be null"); @@ -223,11 +175,6 @@ public SchemaGetFieldTypeInfoResponse getFieldTypeInfo( SolrException.ErrorCode.NOT_FOUND, "No such field type [" + fieldTypeName + "]"); } - public static class SchemaGetFieldTypeInfoResponse extends SolrJerseyResponse { - @JsonProperty("fieldType") - public SimpleOrderedMap fieldTypeInfo; - } - private List listAllFieldsOfType(String realName, SolrParams params) { String camelCaseRealName = IndexSchema.nameMapping.get(realName); Map propertyValues = indexSchema.getNamedPropertyValues(realName, params); diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/NodeLoggingAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/NodeLogging.java similarity index 57% rename from solr/core/src/java/org/apache/solr/handler/admin/api/NodeLoggingAPI.java rename to solr/core/src/java/org/apache/solr/handler/admin/api/NodeLogging.java index bbcd4cc8de4..5594d5f084e 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/NodeLoggingAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/NodeLogging.java @@ -17,29 +17,28 @@ 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.common.SolrException.ErrorCode.BAD_REQUEST; import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM; import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_READ_PERM; -import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.inject.Inject; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.PUT; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import org.apache.solr.api.JerseyResource; -import org.apache.solr.client.api.model.SolrJerseyResponse; +import org.apache.solr.client.api.endpoint.NodeLoggingApis; +import org.apache.solr.client.api.model.ListLevelsResponse; +import org.apache.solr.client.api.model.LogLevelChange; +import org.apache.solr.client.api.model.LogLevelInfo; +import org.apache.solr.client.api.model.LogMessageInfo; +import org.apache.solr.client.api.model.LogMessagesResponse; +import org.apache.solr.client.api.model.LoggingResponse; +import org.apache.solr.client.api.model.SetThresholdRequestBody; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrException; import org.apache.solr.core.CoreContainer; -import org.apache.solr.jersey.JacksonReflectMapWriter; import org.apache.solr.jersey.PermissionName; import org.apache.solr.logging.LogWatcher; import org.slf4j.Logger; @@ -51,8 +50,7 @@ * *

    These APIs ('/api/node/logging' and descendants) are analogous to the v1 /admin/info/logging. */ -@Path("/node/logging") -public class NodeLoggingAPI extends JerseyResource { +public class NodeLogging extends JerseyResource implements NodeLoggingApis { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @@ -60,15 +58,13 @@ public class NodeLoggingAPI extends JerseyResource { private final LogWatcher watcher; @Inject - public NodeLoggingAPI(CoreContainer coreContainer) { + public NodeLogging(CoreContainer coreContainer) { this.coreContainer = coreContainer; this.watcher = coreContainer.getLogging(); } - @Path("/levels") - @GET + @Override @PermissionName(CONFIG_READ_PERM) - @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2}) public ListLevelsResponse listAllLoggersAndLevels() { ensureLogWatcherEnabled(); final ListLevelsResponse response = instantiateLoggingResponse(ListLevelsResponse.class); @@ -86,10 +82,8 @@ public ListLevelsResponse listAllLoggersAndLevels() { return response; } - @Path("/levels") - @PUT + @Override @PermissionName(CONFIG_EDIT_PERM) - @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2}) public LoggingResponse modifyLocalLogLevel(List requestBody) { ensureLogWatcherEnabled(); final LoggingResponse response = instantiateLoggingResponse(LoggingResponse.class); @@ -104,11 +98,9 @@ public LoggingResponse modifyLocalLogLevel(List requestBody) { return response; } - @Path("/messages") - @GET + @Override @PermissionName(CONFIG_READ_PERM) - @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2}) - public LogMessagesResponse fetchLocalLogMessages(@QueryParam("since") Long boundingTimeMillis) { + public LogMessagesResponse fetchLocalLogMessages(Long boundingTimeMillis) { ensureLogWatcherEnabled(); final LogMessagesResponse response = instantiateLoggingResponse(LogMessagesResponse.class); if (boundingTimeMillis == null) { @@ -137,10 +129,8 @@ public LogMessagesResponse fetchLocalLogMessages(@QueryParam("since") Long bound return response; } - @Path("/messages/threshold") - @PUT + @Override @PermissionName(CONFIG_EDIT_PERM) - @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2}) public LoggingResponse setMessageThreshold(SetThresholdRequestBody requestBody) { ensureLogWatcherEnabled(); final LoggingResponse response = instantiateLoggingResponse(LoggingResponse.class); @@ -175,99 +165,19 @@ private T instantiateLoggingResponse(Class clazz) return response; } - /** Generic logging response that includes the name of the log watcher (e.g. "Log4j2") */ - public static class LoggingResponse extends SolrJerseyResponse { - @JsonProperty("watcher") - public String watcherName; - } - - /** A user-requested modification in the level that a specified logger reports at. */ - public static class LogLevelChange implements JacksonReflectMapWriter { - public LogLevelChange() {} - - public LogLevelChange(String logger, String level) { - this.logger = logger; - this.level = level; - } + public static List parseLogLevelChanges(String[] rawChangeValues) { + final List changes = new ArrayList<>(); - @JsonProperty public String logger; - @JsonProperty public String level; - - public static List createRequestBodyFromV1Params(String[] rawChangeValues) { - final List changes = new ArrayList<>(); - - for (String rawChange : rawChangeValues) { - String[] split = rawChange.split(":"); - if (split.length != 2) { - throw new SolrException( - SolrException.ErrorCode.SERVER_ERROR, - "Invalid format, expected level:value, got " + rawChange); - } - changes.add(new LogLevelChange(split[0], split[1])); + for (String rawChange : rawChangeValues) { + String[] split = rawChange.split(":"); + if (split.length != 2) { + throw new SolrException( + SolrException.ErrorCode.SERVER_ERROR, + "Invalid format, expected level:value, got " + rawChange); } - - return changes; - } - } - - /** The request body for the 'PUT /api/node/logging/messages/threshold' API. */ - public static class SetThresholdRequestBody implements JacksonReflectMapWriter { - public SetThresholdRequestBody() {} - - public SetThresholdRequestBody(String level) { - this.level = level; - } - - @JsonProperty(required = true) - public String level; - } - - /** Response format for the 'GET /api/node/logging/messages' API. */ - public static class LogMessagesResponse extends LoggingResponse { - @JsonProperty public LogMessageInfo info; - - @JsonProperty("history") - public SolrDocumentList docs; - } - - /** Metadata about the log messages returned by the 'GET /api/node/logging/messages' API */ - public static class LogMessageInfo implements JacksonReflectMapWriter { - @JsonProperty("since") - public Long boundingTimeMillis; - - @JsonProperty public Boolean found; - @JsonProperty public List levels; - - @JsonProperty("last") - public long lastRecordTimestampMillis; - - @JsonProperty public int buffer; - @JsonProperty public String threshold; - } - - /** Response format for the 'GET /api/node/logging/levels' API. */ - public static class ListLevelsResponse extends LoggingResponse { - @JsonProperty public List levels; - @JsonProperty public List loggers; - } - - /** Representation of a single logger and its current state. */ - public static class LogLevelInfo implements JacksonReflectMapWriter { - public LogLevelInfo() {} - - public LogLevelInfo(String name, String level, boolean set) { - this.name = name; - this.level = level; - this.set = set; + changes.add(new LogLevelChange(split[0], split[1])); } - @JsonProperty("name") - public String name; - - @JsonProperty("level") - public String level; - - @JsonProperty("set") - public boolean set; + return changes; } } diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/ReplicationAPIBase.java b/solr/core/src/java/org/apache/solr/handler/admin/api/ReplicationAPIBase.java index f438ecc2ac7..0f5e9d5dcae 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/ReplicationAPIBase.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/ReplicationAPIBase.java @@ -32,6 +32,9 @@ import org.apache.lucene.store.IOContext; import org.apache.lucene.store.IndexInput; import org.apache.solr.api.JerseyResource; +import org.apache.solr.client.api.model.FileListResponse; +import org.apache.solr.client.api.model.FileMetaData; +import org.apache.solr.client.api.model.IndexVersionResponse; import org.apache.solr.core.DirectoryFactory; import org.apache.solr.core.IndexDeletionPolicyWrapper; import org.apache.solr.core.SolrCore; @@ -56,23 +59,21 @@ public ReplicationAPIBase( this.solrQueryResponse = solrQueryResponse; } - protected CoreReplicationAPI.IndexVersionResponse doFetchIndexVersion() throws IOException { + protected IndexVersionResponse doFetchIndexVersion() throws IOException { ReplicationHandler replicationHandler = (ReplicationHandler) solrCore.getRequestHandler(ReplicationHandler.PATH); return replicationHandler.getIndexVersionResponse(); } - protected CoreReplicationAPI.FileListResponse doFetchFileList(long generation) { + protected FileListResponse doFetchFileList(long generation) { ReplicationHandler replicationHandler = (ReplicationHandler) solrCore.getRequestHandler(ReplicationHandler.PATH); return getFileList(generation, replicationHandler); } - protected CoreReplicationAPI.FileListResponse getFileList( - long generation, ReplicationHandler replicationHandler) { + protected FileListResponse getFileList(long generation, ReplicationHandler replicationHandler) { final IndexDeletionPolicyWrapper delPol = solrCore.getDeletionPolicy(); - final CoreReplicationAPI.FileListResponse filesResponse = - new CoreReplicationAPI.FileListResponse(); + final FileListResponse filesResponse = new FileListResponse(); IndexCommit commit = null; try { @@ -96,7 +97,7 @@ protected CoreReplicationAPI.FileListResponse getFileList( } assert null != commit; - List result = new ArrayList<>(); + List result = new ArrayList<>(); Directory dir = null; try { dir = @@ -109,7 +110,7 @@ protected CoreReplicationAPI.FileListResponse getFileList( SegmentInfos infos = SegmentInfos.readCommit(dir, commit.getSegmentsFileName()); for (SegmentCommitInfo commitInfo : infos) { for (String file : commitInfo.files()) { - CoreReplicationAPI.FileMetaData metaData = new CoreReplicationAPI.FileMetaData(); + FileMetaData metaData = new FileMetaData(); metaData.name = file; metaData.size = dir.fileLength(file); @@ -127,7 +128,7 @@ protected CoreReplicationAPI.FileListResponse getFileList( } // add the segments_N file - CoreReplicationAPI.FileMetaData fileMetaData = new CoreReplicationAPI.FileMetaData(); + FileMetaData fileMetaData = new FileMetaData(); fileMetaData.name = infos.getSegmentsFileName(); fileMetaData.size = dir.fileLength(infos.getSegmentsFileName()); if (infos.getId() != null) { @@ -186,7 +187,7 @@ protected CoreReplicationAPI.FileListResponse getFileList( } private void reportErrorOnResponse( - CoreReplicationAPI.FileListResponse fileListResponse, String message, Exception e) { + FileListResponse fileListResponse, String message, Exception e) { fileListResponse.status = ERR_STATUS; fileListResponse.message = message; if (e != null) { diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/CoreReplicationAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/CoreReplicationAPITest.java index 19fd1ea96e2..bfde05348d5 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/CoreReplicationAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/CoreReplicationAPITest.java @@ -25,6 +25,9 @@ import java.util.Arrays; import java.util.List; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.api.model.FileListResponse; +import org.apache.solr.client.api.model.FileMetaData; +import org.apache.solr.client.api.model.IndexVersionResponse; import org.apache.solr.core.SolrCore; import org.apache.solr.handler.ReplicationHandler; import org.apache.solr.request.SolrQueryRequest; @@ -33,10 +36,10 @@ import org.junit.BeforeClass; import org.junit.Test; -/** Unit tests for {@link CoreReplicationAPI} */ +/** Unit tests for {@link CoreReplication} */ public class CoreReplicationAPITest extends SolrTestCaseJ4 { - private CoreReplicationAPI coreReplicationAPI; + private CoreReplication coreReplicationAPI; private SolrCore mockCore; private ReplicationHandler mockReplicationHandler; private SolrQueryRequest mockQueryRequest; @@ -60,11 +63,10 @@ public void setUp() throws Exception { @Test public void testGetIndexVersion() throws Exception { - CoreReplicationAPI.IndexVersionResponse expected = - new CoreReplicationAPI.IndexVersionResponse(123L, 123L, "testGeneration"); + IndexVersionResponse expected = new IndexVersionResponse(123L, 123L, "testGeneration"); when(mockReplicationHandler.getIndexVersionResponse()).thenReturn(expected); - CoreReplicationAPI.IndexVersionResponse actual = coreReplicationAPI.doFetchIndexVersion(); + IndexVersionResponse actual = coreReplicationAPI.doFetchIndexVersion(); assertEquals(expected.indexVersion, actual.indexVersion); assertEquals(expected.generation, actual.generation); assertEquals(expected.status, actual.status); @@ -73,7 +75,7 @@ public void testGetIndexVersion() throws Exception { @Test @SuppressWarnings("unchecked") public void testFetchFiles() throws Exception { - CoreReplicationAPI.FileListResponse actualResponse = coreReplicationAPI.fetchFileList(-1); + FileListResponse actualResponse = coreReplicationAPI.fetchFileList(-1); assertEquals(123, actualResponse.fileList.get(0).size); assertEquals("test", actualResponse.fileList.get(0).name); assertEquals(123456789, actualResponse.fileList.get(0).checksum); @@ -85,7 +87,7 @@ private void setUpMocks() { when(mockCore.getRequestHandler(ReplicationHandler.PATH)).thenReturn(mockReplicationHandler); } - private static class CoreReplicationAPIMock extends CoreReplicationAPI { + private static class CoreReplicationAPIMock extends CoreReplication { public CoreReplicationAPIMock(SolrCore solrCore, SolrQueryRequest req, SolrQueryResponse rsp) { super(solrCore, req, rsp); } 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 e7d1ff89001..b4f5e2d2760 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,12 +24,16 @@ import java.util.List; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.api.model.CategoryRoutedAliasProperties; +import org.apache.solr.client.api.model.CreateAliasRequestBody; import org.apache.solr.client.api.model.CreateCollectionRequestBody; +import org.apache.solr.client.api.model.RoutedAliasProperties; +import org.apache.solr.client.api.model.TimeRoutedAliasProperties; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.ModifiableSolrParams; import org.junit.Test; -/** Unit tests for {@link CreateAliasAPI} */ +/** Unit tests for {@link CreateAlias} */ public class CreateAliasAPITest extends SolrTestCaseJ4 { @Test @@ -38,7 +42,7 @@ public void testReportsErrorIfRequestBodyMissing() { expectThrows( SolrException.class, () -> { - final var api = new CreateAliasAPI(null, null, null); + final var api = new CreateAlias(null, null, null); api.createAlias(null); }); @@ -48,11 +52,12 @@ public void testReportsErrorIfRequestBodyMissing() { @Test public void testReportsErrorIfAliasNameInvalid() { - final var requestBody = new CreateAliasAPI.CreateAliasRequestBody(); + final var requestBody = new CreateAliasRequestBody(); requestBody.name = "some@invalid$alias"; requestBody.collections = List.of("validColl1", "validColl2"); - final var thrown = expectThrows(SolrException.class, () -> requestBody.validate()); + final var thrown = + expectThrows(SolrException.class, () -> CreateAlias.validateRequestBody(requestBody)); assertThat(thrown.getMessage(), containsString("Invalid alias")); assertThat(thrown.getMessage(), containsString("some@invalid$alias")); assertThat( @@ -64,24 +69,26 @@ public void testReportsErrorIfAliasNameInvalid() { // Aliases can be normal or "routed', but not both. @Test public void testReportsErrorIfExplicitCollectionsAndRoutingParamsBothProvided() { - final var requestBody = new CreateAliasAPI.CreateAliasRequestBody(); + final var requestBody = new CreateAliasRequestBody(); requestBody.name = "validName"; requestBody.collections = List.of("validColl1"); - final var categoryRouter = new CreateAliasAPI.CategoryRoutedAliasProperties(); + final var categoryRouter = new CategoryRoutedAliasProperties(); categoryRouter.field = "someField"; requestBody.routers = List.of(categoryRouter); - final var thrown = expectThrows(SolrException.class, () -> requestBody.validate()); + final var thrown = + expectThrows(SolrException.class, () -> CreateAlias.validateRequestBody(requestBody)); assertEquals( "Collections cannot be specified when creating a routed alias.", thrown.getMessage()); } @Test public void testReportsErrorIfNeitherExplicitCollectionsNorRoutingParamsProvided() { - final var requestBody = new CreateAliasAPI.CreateAliasRequestBody(); + final var requestBody = new CreateAliasRequestBody(); requestBody.name = "validName"; - final var thrown = expectThrows(SolrException.class, () -> requestBody.validate()); + final var thrown = + expectThrows(SolrException.class, () -> CreateAlias.validateRequestBody(requestBody)); assertEquals(400, thrown.code()); assertEquals( "Alias creation requires either a list of either collections (for creating a traditional alias) or routers (for creating a routed alias)", @@ -90,16 +97,17 @@ public void testReportsErrorIfNeitherExplicitCollectionsNorRoutingParamsProvided @Test public void testRoutedAliasesMustProvideAConfigsetToUseOnCreatedCollections() { - final var requestBody = new CreateAliasAPI.CreateAliasRequestBody(); + final var requestBody = new CreateAliasRequestBody(); requestBody.name = "validName"; - final var categoryRouter = new CreateAliasAPI.CategoryRoutedAliasProperties(); + final var categoryRouter = new CategoryRoutedAliasProperties(); categoryRouter.field = "someField"; requestBody.routers = List.of(categoryRouter); final var createParams = new CreateCollectionRequestBody(); createParams.numShards = 3; requestBody.collCreationParameters = createParams; - final var thrown = expectThrows(SolrException.class, () -> requestBody.validate()); + final var thrown = + expectThrows(SolrException.class, () -> CreateAlias.validateRequestBody(requestBody)); assertEquals(400, thrown.code()); assertThat( thrown.getMessage(), containsString("Routed alias creation requires a configset name")); @@ -107,9 +115,9 @@ public void testRoutedAliasesMustProvideAConfigsetToUseOnCreatedCollections() { @Test public void testRoutedAliasesMustNotSpecifyANameInCollectionCreationParams() { - final var requestBody = new CreateAliasAPI.CreateAliasRequestBody(); + final var requestBody = new CreateAliasRequestBody(); requestBody.name = "validName"; - final var categoryRouter = new CreateAliasAPI.CategoryRoutedAliasProperties(); + final var categoryRouter = new CategoryRoutedAliasProperties(); categoryRouter.field = "someField"; requestBody.routers = List.of(categoryRouter); final var createParams = new CreateCollectionRequestBody(); @@ -120,7 +128,8 @@ public void testRoutedAliasesMustNotSpecifyANameInCollectionCreationParams() { createParams.name = "someCollectionName"; requestBody.collCreationParameters = createParams; - final var thrown = expectThrows(SolrException.class, () -> requestBody.validate()); + final var thrown = + expectThrows(SolrException.class, () -> CreateAlias.validateRequestBody(requestBody)); assertEquals(400, thrown.code()); assertThat(thrown.getMessage(), containsString("cannot specify the name")); @@ -128,17 +137,18 @@ public void testRoutedAliasesMustNotSpecifyANameInCollectionCreationParams() { @Test public void testReportsErrorIfCategoryRoutedAliasDoesntSpecifyAllRequiredParameters() { - final var requestBody = new CreateAliasAPI.CreateAliasRequestBody(); + final var requestBody = new CreateAliasRequestBody(); requestBody.name = "validName"; final var createParams = new CreateCollectionRequestBody(); createParams.numShards = 3; createParams.config = "someConfig"; requestBody.collCreationParameters = createParams; - final var categoryRouter = new CreateAliasAPI.CategoryRoutedAliasProperties(); + final var categoryRouter = new CategoryRoutedAliasProperties(); categoryRouter.maxCardinality = 123L; requestBody.routers = List.of(categoryRouter); - final var thrown = expectThrows(SolrException.class, () -> requestBody.validate()); + final var thrown = + expectThrows(SolrException.class, () -> CreateAlias.validateRequestBody(requestBody)); assertEquals(400, thrown.code()); assertEquals( @@ -149,12 +159,13 @@ public void testReportsErrorIfCategoryRoutedAliasDoesntSpecifyAllRequiredParamet public void testReportsErrorIfTimeRoutedAliasDoesntSpecifyAllRequiredParameters() { // No 'field' defined! { - final var timeRouter = new CreateAliasAPI.TimeRoutedAliasProperties(); + final var timeRouter = new TimeRoutedAliasProperties(); timeRouter.start = "NOW"; timeRouter.interval = "+5MINUTES"; final var requestBody = requestBodyWithProvidedRouter(timeRouter); - final var thrown = expectThrows(SolrException.class, () -> requestBody.validate()); + final var thrown = + expectThrows(SolrException.class, () -> CreateAlias.validateRequestBody(requestBody)); assertEquals(400, thrown.code()); assertEquals("Missing required parameter: 'field' on time routed alias", thrown.getMessage()); @@ -162,12 +173,13 @@ public void testReportsErrorIfTimeRoutedAliasDoesntSpecifyAllRequiredParameters( // No 'start' defined! { - final var timeRouter = new CreateAliasAPI.TimeRoutedAliasProperties(); + final var timeRouter = new TimeRoutedAliasProperties(); timeRouter.field = "someField"; timeRouter.interval = "+5MINUTES"; final var requestBody = requestBodyWithProvidedRouter(timeRouter); - final var thrown = expectThrows(SolrException.class, () -> requestBody.validate()); + final var thrown = + expectThrows(SolrException.class, () -> CreateAlias.validateRequestBody(requestBody)); assertEquals(400, thrown.code()); assertEquals("Missing required parameter: 'start' on time routed alias", thrown.getMessage()); @@ -175,12 +187,13 @@ public void testReportsErrorIfTimeRoutedAliasDoesntSpecifyAllRequiredParameters( // No 'interval' defined! { - final var timeRouter = new CreateAliasAPI.TimeRoutedAliasProperties(); + final var timeRouter = new TimeRoutedAliasProperties(); timeRouter.field = "someField"; timeRouter.start = "NOW"; final var requestBody = requestBodyWithProvidedRouter(timeRouter); - final var thrown = expectThrows(SolrException.class, () -> requestBody.validate()); + final var thrown = + expectThrows(SolrException.class, () -> CreateAlias.validateRequestBody(requestBody)); assertEquals(400, thrown.code()); assertEquals( @@ -190,13 +203,13 @@ public void testReportsErrorIfTimeRoutedAliasDoesntSpecifyAllRequiredParameters( @Test public void testRemoteMessageCreationForTraditionalAlias() { - final var requestBody = new CreateAliasAPI.CreateAliasRequestBody(); + final var requestBody = new CreateAliasRequestBody(); requestBody.name = "someAliasName"; requestBody.collections = List.of("validColl1", "validColl2"); requestBody.async = "someAsyncId"; final var remoteMessage = - CreateAliasAPI.createRemoteMessageForTraditionalAlias(requestBody).getProperties(); + CreateAlias.createRemoteMessageForTraditionalAlias(requestBody).getProperties(); assertEquals(4, remoteMessage.size()); assertEquals("createalias", remoteMessage.get(QUEUE_OPERATION)); @@ -207,9 +220,9 @@ public void testRemoteMessageCreationForTraditionalAlias() { @Test public void testRemoteMessageCreationForCategoryRoutedAlias() { - final var requestBody = new CreateAliasAPI.CreateAliasRequestBody(); + final var requestBody = new CreateAliasRequestBody(); requestBody.name = "someAliasName"; - final var categoryRouter = new CreateAliasAPI.CategoryRoutedAliasProperties(); + final var categoryRouter = new CategoryRoutedAliasProperties(); categoryRouter.field = "someField"; requestBody.routers = List.of(categoryRouter); final var createParams = new CreateCollectionRequestBody(); @@ -218,7 +231,7 @@ public void testRemoteMessageCreationForCategoryRoutedAlias() { requestBody.collCreationParameters = createParams; final var remoteMessage = - CreateAliasAPI.createRemoteMessageForRoutedAlias(requestBody).getProperties(); + CreateAlias.createRemoteMessageForRoutedAlias(requestBody).getProperties(); assertEquals(6, remoteMessage.size()); assertEquals("createalias", remoteMessage.get(QUEUE_OPERATION)); @@ -231,9 +244,9 @@ public void testRemoteMessageCreationForCategoryRoutedAlias() { @Test public void testRemoteMessageCreationForTimeRoutedAlias() { - final var requestBody = new CreateAliasAPI.CreateAliasRequestBody(); + final var requestBody = new CreateAliasRequestBody(); requestBody.name = "someAliasName"; - final var timeRouter = new CreateAliasAPI.TimeRoutedAliasProperties(); + final var timeRouter = new TimeRoutedAliasProperties(); timeRouter.field = "someField"; timeRouter.start = "NOW"; timeRouter.interval = "+1MONTH"; @@ -245,7 +258,7 @@ public void testRemoteMessageCreationForTimeRoutedAlias() { requestBody.collCreationParameters = createParams; final var remoteMessage = - CreateAliasAPI.createRemoteMessageForRoutedAlias(requestBody).getProperties(); + CreateAlias.createRemoteMessageForRoutedAlias(requestBody).getProperties(); assertEquals(9, remoteMessage.size()); assertEquals("createalias", remoteMessage.get(QUEUE_OPERATION)); @@ -261,14 +274,14 @@ public void testRemoteMessageCreationForTimeRoutedAlias() { @Test public void testRemoteMessageCreationForMultiDimensionalRoutedAlias() { - final var requestBody = new CreateAliasAPI.CreateAliasRequestBody(); + final var requestBody = new CreateAliasRequestBody(); requestBody.name = "someAliasName"; - final var timeRouter = new CreateAliasAPI.TimeRoutedAliasProperties(); + final var timeRouter = new TimeRoutedAliasProperties(); timeRouter.field = "someField"; timeRouter.start = "NOW"; timeRouter.interval = "+1MONTH"; timeRouter.maxFutureMs = 123456L; - final var categoryRouter = new CreateAliasAPI.CategoryRoutedAliasProperties(); + final var categoryRouter = new CategoryRoutedAliasProperties(); categoryRouter.field = "someField"; requestBody.routers = List.of(timeRouter, categoryRouter); final var createParams = new CreateCollectionRequestBody(); @@ -277,7 +290,7 @@ public void testRemoteMessageCreationForMultiDimensionalRoutedAlias() { requestBody.collCreationParameters = createParams; final var remoteMessage = - CreateAliasAPI.createRemoteMessageForRoutedAlias(requestBody).getProperties(); + CreateAlias.createRemoteMessageForRoutedAlias(requestBody).getProperties(); assertEquals(11, remoteMessage.size()); assertEquals("createalias", remoteMessage.get(QUEUE_OPERATION)); @@ -293,9 +306,8 @@ public void testRemoteMessageCreationForMultiDimensionalRoutedAlias() { assertEquals("someConfig", remoteMessage.get("create-collection.collection.configName")); } - private CreateAliasAPI.CreateAliasRequestBody requestBodyWithProvidedRouter( - CreateAliasAPI.RoutedAliasProperties router) { - final var requestBody = new CreateAliasAPI.CreateAliasRequestBody(); + private CreateAliasRequestBody requestBodyWithProvidedRouter(RoutedAliasProperties router) { + final var requestBody = new CreateAliasRequestBody(); requestBody.name = "validName"; final var createParams = new CreateCollectionRequestBody(); createParams.numShards = 3; @@ -321,23 +333,22 @@ public void testConvertsV1ParamsForMultiDimensionalAliasToV2RequestBody() { v1Params.add("create-collection.numShards", "3"); v1Params.add("create-collection.collection.configName", "someConfig"); - final var requestBody = CreateAliasAPI.createFromSolrParams(v1Params); + final var requestBody = CreateAlias.createFromSolrParams(v1Params); assertEquals("someAliasName", requestBody.name); assertEquals(2, requestBody.routers.size()); assertTrue( "Incorrect router type " + requestBody.routers.get(0) + " at index 0", - requestBody.routers.get(0) instanceof CreateAliasAPI.TimeRoutedAliasProperties); - final var timeRouter = (CreateAliasAPI.TimeRoutedAliasProperties) requestBody.routers.get(0); + requestBody.routers.get(0) instanceof TimeRoutedAliasProperties); + final var timeRouter = (TimeRoutedAliasProperties) requestBody.routers.get(0); assertEquals("someField", timeRouter.field); assertEquals("NOW", timeRouter.start); assertEquals("+1MONTH", timeRouter.interval); assertEquals(Long.valueOf(123456L), timeRouter.maxFutureMs); assertTrue( "Incorrect router type " + requestBody.routers.get(1) + " at index 1", - requestBody.routers.get(1) instanceof CreateAliasAPI.CategoryRoutedAliasProperties); - final var categoryRouter = - (CreateAliasAPI.CategoryRoutedAliasProperties) requestBody.routers.get(1); + requestBody.routers.get(1) instanceof CategoryRoutedAliasProperties); + final var categoryRouter = (CategoryRoutedAliasProperties) requestBody.routers.get(1); assertEquals("someOtherField", categoryRouter.field); assertEquals(Long.valueOf(20), categoryRouter.maxCardinality); final var createCollParams = requestBody.collCreationParameters; diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaFieldsAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaFieldsAPITest.java index 03e134602a0..3c5a9f0a0de 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaFieldsAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaFieldsAPITest.java @@ -30,13 +30,13 @@ import org.junit.Before; import org.junit.Test; -/** Unit tests for {@link GetSchemaFieldAPI} */ +/** Unit tests for {@link GetSchemaField} */ @SuppressWarnings("unchecked") public class GetSchemaFieldsAPITest extends SolrTestCaseJ4 { private IndexSchema mockSchema; private SolrParams mockParams; - private GetSchemaFieldAPI api; + private GetSchemaField api; private SimpleOrderedMap mockField; private ArrayList> mockFieldList; @@ -47,7 +47,7 @@ public void setUpMocks() { mockSchema = mock(IndexSchema.class); mockParams = mock(SolrParams.class); - api = new GetSchemaFieldAPI(mockSchema, mockParams); + api = new GetSchemaField(mockSchema, mockParams); mockField = new SimpleOrderedMap<>(); mockField.add("name", "id"); @@ -76,7 +76,7 @@ public void testReliesOnIndexSchemaWhenFetchingSpecificField() { final var response = api.getFieldInfo("id"); assertNotNull(response); - assertCorrectField(response.fieldInfo); + assertCorrectField((SimpleOrderedMap) response.fieldInfo); } @Test @@ -109,7 +109,7 @@ public void testReliesOnIndexSchemaWhenFetchingSpecificDynamicField() { final var response = api.getDynamicFieldInfo("id"); assertNotNull(response); - assertCorrectField(response.dynamicFieldInfo); + assertCorrectField((SimpleOrderedMap) response.dynamicFieldInfo); } @Test @@ -131,7 +131,7 @@ public void testReliesOnIndexSchemaWhenFetchingSpecificFieldType() { final var response = api.getFieldTypeInfo("id"); assertNotNull(response); - assertCorrectField(response.fieldTypeInfo); + assertCorrectField((SimpleOrderedMap) response.fieldTypeInfo); } private void assertCorrectListFields(List responseFields) { diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/NodeLoggingAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/NodeLoggingAPITest.java index 1572812cb06..8838ac9e483 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/NodeLoggingAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/NodeLoggingAPITest.java @@ -19,6 +19,7 @@ import static org.apache.solr.SolrTestCaseJ4.assumeWorkingMockito; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.instanceOf; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -27,6 +28,8 @@ import java.util.List; import org.apache.solr.SolrTestCase; +import org.apache.solr.client.api.model.LogLevelChange; +import org.apache.solr.client.api.model.SetThresholdRequestBody; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.core.CoreContainer; @@ -36,7 +39,7 @@ import org.junit.BeforeClass; import org.junit.Test; -/** Unit tests for the functionality offered in {@link NodeLoggingAPI} */ +/** Unit tests for the functionality offered in {@link NodeLogging} */ @SuppressWarnings({"unchecked", "rawtypes"}) public class NodeLoggingAPITest extends SolrTestCase { @@ -61,7 +64,7 @@ public void testReliesOnLogWatcherToListLogLevels() { .thenReturn(List.of("ERROR", "WARN", "INFO", "DEBUG", "TRACE")); when(mockLogWatcher.getAllLoggers()) .thenReturn(List.of(logInfo("org.a.s.Foo", "WARN", true), logInfo("org", null, false))); - final var responseBody = new NodeLoggingAPI(mockCoreContainer).listAllLoggersAndLevels(); + final var responseBody = new NodeLogging(mockCoreContainer).listAllLoggersAndLevels(); assertEquals(5, responseBody.levels.size()); assertThat(responseBody.levels, containsInAnyOrder("ERROR", "WARN", "INFO", "DEBUG", "TRACE")); @@ -80,8 +83,8 @@ public void testReliesOnLogWatcherToListLogLevels() { @Test public void testReliesOnLogWatcherToModifyLogLevels() { final var responseBody = - new NodeLoggingAPI(mockCoreContainer) - .modifyLocalLogLevel(List.of(new NodeLoggingAPI.LogLevelChange("o.a.s.Foo", "WARN"))); + new NodeLogging(mockCoreContainer) + .modifyLocalLogLevel(List.of(new LogLevelChange("o.a.s.Foo", "WARN"))); assertNotNull(responseBody); assertNull("Expected error to be null but was " + responseBody.error, responseBody.error); @@ -105,7 +108,7 @@ public void testReliesOnLogWatcherToFetchLogMessages() { when(mockLogWatcher.getLastEvent()).thenReturn(123456L); when(mockLogWatcher.getHistorySize()).thenReturn(321); - final var responseBody = new NodeLoggingAPI(mockCoreContainer).fetchLocalLogMessages(123L); + final var responseBody = new NodeLogging(mockCoreContainer).fetchLocalLogMessages(123L); assertNotNull(responseBody); assertNull("Expected error to be null but was " + responseBody.error, responseBody.error); @@ -113,16 +116,17 @@ public void testReliesOnLogWatcherToFetchLogMessages() { assertEquals(Long.valueOf(123), responseBody.info.boundingTimeMillis); assertEquals(123456, responseBody.info.lastRecordTimestampMillis); assertEquals(321, responseBody.info.buffer); - assertEquals(2, responseBody.docs.size()); - assertEquals("logmsg1", responseBody.docs.get(0).getFieldValue("message_s")); - assertEquals("logmsg2", responseBody.docs.get(1).getFieldValue("message_s")); + assertThat(responseBody.docs, instanceOf(SolrDocumentList.class)); + final var docList = (SolrDocumentList) responseBody.docs; + assertEquals(2, docList.size()); + assertEquals("logmsg1", docList.get(0).getFieldValue("message_s")); + assertEquals("logmsg2", docList.get(1).getFieldValue("message_s")); } @Test public void testReliesOnLogWatcherToSetMessageThreshold() { final var responseBody = - new NodeLoggingAPI(mockCoreContainer) - .setMessageThreshold(new NodeLoggingAPI.SetThresholdRequestBody("WARN")); + new NodeLogging(mockCoreContainer).setMessageThreshold(new SetThresholdRequestBody("WARN")); assertNotNull(responseBody); assertNull("Expected error to be null but was " + responseBody.error, responseBody.error); @@ -134,8 +138,7 @@ public void testReliesOnLogWatcherToSetMessageThreshold() { @Test public void testCorrectlyParsesV1LogLevelChanges() { final var levelChanges = - NodeLoggingAPI.LogLevelChange.createRequestBodyFromV1Params( - new String[] {"o.a.s.Foo:WARN", "o.a.s.Bar:INFO"}); + NodeLogging.parseLogLevelChanges(new String[] {"o.a.s.Foo:WARN", "o.a.s.Bar:INFO"}); assertEquals(2, levelChanges.size()); assertEquals("o.a.s.Foo", levelChanges.get(0).logger); diff --git a/solr/solrj/src/resources/java-template/api.mustache b/solr/solrj/src/resources/java-template/api.mustache index c3961cf50b1..757335a2b83 100644 --- a/solr/solrj/src/resources/java-template/api.mustache +++ b/solr/solrj/src/resources/java-template/api.mustache @@ -48,9 +48,21 @@ import {{import}}; {{#operation}} {{#bodyParam}} {{#vars}} + +{{! Catches all model types used "directly" (i.e. not in a container) in request/response classes. }} +{{#isModel}} +import {{modelPackage}}.{{dataType}}; +{{/isModel}} + +{{! Catches all model types used in a List, Map, or other container in request/response classes. }} +{{#isContainer}} +{{#items}} {{#isModel}} import {{modelPackage}}.{{dataType}}; {{/isModel}} +{{/items}} +{{/isContainer}} + {{/vars}} {{/bodyParam}} {{/operation}} @@ -121,12 +133,27 @@ public class {{classname}} { {{/requiredParams}} {{#bodyParam}} {{^vendorExtensions.x-genericEntity}} + {{#isArray}} + this.requestBody = new ArrayList<>(); + addHeader("Content-type", "application/json"); + {{/isArray}} + {{^isArray}} this.requestBody = new {{{dataType}}}(); addHeader("Content-type", "application/json"); + {{/isArray}} {{/vendorExtensions.x-genericEntity}} {{/bodyParam}} } + {{! If the request body is a list, provide methods to add to it}} + {{#bodyParam}} + {{#isArray}} + public void add{{items.dataType}}({{items.dataType}} entry) { + this.requestBody.add(entry); + } + {{/isArray}} + {{/bodyParam}} + {{#optionalParams}} {{^isBodyParam}} {{#description}}