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}}