diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/SelectApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/SelectApi.java new file mode 100644 index 00000000000..94a7e3fb590 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/SelectApi.java @@ -0,0 +1,76 @@ +/* + * 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 static org.apache.solr.client.api.util.Constants.GENERIC_ENTITY_PROPERTY; +import static org.apache.solr.client.api.util.Constants.STORE_PATH_PREFIX; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.extensions.Extension; +import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import java.io.InputStream; +import java.util.List; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; +import org.apache.solr.client.api.model.FlexibleSolrJerseyResponse; +import org.apache.solr.client.api.util.StoreApiParameters; + +/** + * V2 API implementation shim for Solr's querying or otherwise inspecting documents in a core or + * collection. + * + *

Due to the complexity and configurability of Solr's '/select' endpoint, this interface doesn't + * attempt to be exhaustive in describing /select inputs and outputs. Rather, it exists to give + * Solr's OAS (and the clients generated from that) an approximate view of the endpoint until its + * inputs and outputs can be understood more fully. + */ +@Path(STORE_PATH_PREFIX + "/select") +public interface SelectApi { + @GET + @StoreApiParameters + @Operation( + summary = "Query a Solr core or collection using individual query parameters", + tags = {"querying"}) + FlexibleSolrJerseyResponse query( + @QueryParam("q") String query, + @QueryParam("fq") List filterQueries, + @QueryParam("fl") String fieldList, + @QueryParam("rows") Integer rows); + + @POST + @StoreApiParameters + @Operation( + summary = "Query a Solr core or collection using the structured request DSL", + tags = {"querying"}) + // TODO Find way to bundle the request-body annotations below for re-use on other similar + // endpoints. + FlexibleSolrJerseyResponse jsonQuery( + @Parameter(required = true) + @RequestBody( + required = true, + extensions = { + @Extension( + properties = { + @ExtensionProperty(name = GENERIC_ENTITY_PROPERTY, value = "true") + }) + }) + InputStream structuredRequest); +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/FlexibleSolrJerseyResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/FlexibleSolrJerseyResponse.java new file mode 100644 index 00000000000..013e1ff98f6 --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/FlexibleSolrJerseyResponse.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.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import java.util.HashMap; +import java.util.Map; + +/** A {@link SolrJerseyResponse} which can accept any top-level properties. */ +public class FlexibleSolrJerseyResponse extends SolrJerseyResponse { + + private Map unknownFields = new HashMap<>(); + + @JsonAnyGetter + public Map unknownProperties() { + return unknownFields; + } + + @JsonAnySetter + public void setUnknownProperty(String field, Object value) { + unknownFields.put(field, value); + } +} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/StoreType.java b/solr/api/src/java/org/apache/solr/client/api/model/StoreType.java index 9e88a289485..d6ba2002f4c 100644 --- a/solr/api/src/java/org/apache/solr/client/api/model/StoreType.java +++ b/solr/api/src/java/org/apache/solr/client/api/model/StoreType.java @@ -17,6 +17,17 @@ package org.apache.solr.client.api.model; public enum StoreType { - COLLECTION, - CORE + COLLECTION("collections"), + CORE("cores"); + + private final String pathString; + + StoreType(String pathString) { + this.pathString = pathString; + } + + @Override + public String toString() { + return pathString; + } } 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 f8368a7b19b..cacc7a50090 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,5 +27,7 @@ private Constants() { public static final String STORE_PATH_PREFIX = "/{" + STORE_TYPE_PATH_PARAMETER + ":cores|collections}/{" + STORE_NAME_PATH_PARAMETER + "}"; + public static final String GENERIC_ENTITY_PROPERTY = "genericEntity"; + public static final String BINARY_CONTENT_TYPE_V2 = "application/vnd.apache.solr.javabin"; } diff --git a/solr/solrj/src/resources/java-template/api.mustache b/solr/solrj/src/resources/java-template/api.mustache index 0767d51e09b..95fc0397188 100644 --- a/solr/solrj/src/resources/java-template/api.mustache +++ b/solr/solrj/src/resources/java-template/api.mustache @@ -57,6 +57,16 @@ import {{modelPackage}}.{{dataType}}; // WARNING: This class is generated from a Mustache template; any intended // changes should be made to the underlying template and not this file directly. +/** + * Experimental SolrRequest's and SolrResponse's for {{classVarName}}, generated from an OAS. + * + *

See individual request and response classes for more detailed and relevant information. + * + *

All SolrRequest implementations rely on v2 APIs which may require a SolrClient configured to + * use the '/api' path prefix, instead of '/solr'. + * + * @lucene.experimental + */ public class {{classname}} { {{#operation}} @@ -87,21 +97,29 @@ public class {{classname}} { {{#requiredParams}}{{^isBodyParam}}* @param {{paramName}} Path param - {{description}}{{/isBodyParam}} {{/requiredParams}} */ - public {{operationIdCamelCase}}({{#requiredParams}}{{^isBodyParam}}{{^-first}}, {{/-first}}{{{dataType}}} {{paramName}}{{/isBodyParam}}{{/requiredParams}}) { + public {{operationIdCamelCase}}({{#allParams}}{{#required}}{{^isBodyParam}}{{^-first}}, {{/-first}}{{{dataType}}} {{paramName}}{{/isBodyParam}}{{#isBodyParam}}{{#vendorExtensions.x-genericEntity}}{{^-first}}, {{/-first}}{{{dataType}}} requestBody{{/vendorExtensions.x-genericEntity}}{{/isBodyParam}}{{/required}}{{/allParams}}) { super( SolrRequest.METHOD.valueOf("{{httpMethod}}"), "{{{path}}}"{{#pathParams}} - .replace("{" + "{{baseName}}" + "}", {{paramName}}{{#isEnumRef}}.name(){{/isEnumRef}}){{/pathParams}} + .replace("{" + "{{baseName}}" + "}", {{paramName}}{{^isString}}.toString(){{/isString}}){{/pathParams}} ); {{#requiredParams}} + {{#isBodyParam}} + {{#vendorExtensions.x-genericEntity}} + this.requestBody = requestBody; + addHeader("Content-type", "application/json"); + {{/vendorExtensions.x-genericEntity}} + {{/isBodyParam}} {{^isBodyParam}} this.{{paramName}} = {{paramName}}; {{/isBodyParam}} {{/requiredParams}} {{#bodyParam}} - this.requestBody = new {{baseName}}(); + {{^vendorExtensions.x-genericEntity}} + this.requestBody = new {{{dataType}}}(); addHeader("Content-type", "application/json"); + {{/vendorExtensions.x-genericEntity}} {{/bodyParam}} } @@ -133,7 +151,13 @@ public class {{classname}} { {{/vars}} @Override + @SuppressWarnings("unchecked") public RequestWriter.ContentWriter getContentWriter(String expectedType) { + {{#vendorExtensions.x-genericEntity}} + if (requestBody instanceof String) { + return new RequestWriter.StringPayloadContentWriter((String) requestBody, expectedType); + } + {{/vendorExtensions.x-genericEntity}} return new JacksonContentWriter(expectedType, requestBody); } {{/bodyParam}} @@ -149,7 +173,8 @@ public class {{classname}} { final ModifiableSolrParams params = new ModifiableSolrParams(); {{#queryParams}} if ({{paramName}} != null) { - params.add("{{baseName}}", {{paramName}}{{^isString}}.toString(){{/isString}}); + {{#isArray}}{{paramName}}.stream().forEach(v -> params.add("{{baseName}}", v{{^items.isString}}.toString(){{/items.isString}}));{{/isArray}} + {{^isArray}}params.add("{{baseName}}", {{paramName}}{{^isString}}.toString(){{/isString}});{{/isArray}} } {{/queryParams}} return params;