This is an automated email from the ASF dual-hosted git repository.
Lukas-Finster pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
The following commit(s) were added to refs/heads/trunk by this push:
new c099cac1b8 Implemented: Introduces two new parameters to rest-api.xml
definition (OFBIZ-13494)
c099cac1b8 is described below
commit c099cac1b8ab8aecf649ad638f33f6e16af2a95a
Author: Lukas Finster <[email protected]>
AuthorDate: Tue Aug 18 11:45:37 2026 +0200
Implemented: Introduces two new parameters to rest-api.xml definition
(OFBIZ-13494)
* queryParam: Define a URL queryParameter that gets used as
inputParameter for a service InputParameter
* example: Allows to add a custom response example to be displayed in
swagger.ui
---
.../rest-api/api/exampleApiDefinition.rest.xml | 24 +++++
framework/rest-api/dtd/rest-api.xsd | 37 ++++++++
framework/rest-api/servicedef/services.xml | 7 ++
.../apache/ofbiz/ws/rs/model/ModelApiReader.java | 23 +++++
.../org/apache/ofbiz/ws/rs/model/ModelExample.java | 99 ++++++++++++++++++++
.../apache/ofbiz/ws/rs/model/ModelOperation.java | 102 ++++++++++++++++++++-
.../ofbiz/ws/rs/openapi/OFBizOpenApiReader.java | 71 +++++++++-----
.../org/apache/ofbiz/ws/rs/util/OpenApiUtil.java | 50 ++++++++--
.../ofbiz/ws/rs/test/RestTestHttpRequest.java | 51 +++++++++++
.../apache/ofbiz/ws/rs/test/RestTestServices.java | 8 ++
.../java/org/apache/ofbiz/service/ModelParam.java | 8 ++
11 files changed, 446 insertions(+), 34 deletions(-)
diff --git a/framework/rest-api/api/exampleApiDefinition.rest.xml
b/framework/rest-api/api/exampleApiDefinition.rest.xml
index bdac7a3412..7d4266f748 100644
--- a/framework/rest-api/api/exampleApiDefinition.rest.xml
+++ b/framework/rest-api/api/exampleApiDefinition.rest.xml
@@ -28,6 +28,17 @@ under the License.
<resource name="orders" path="/createRestOrderExample">
<operation verb="post" consumes="application/json"
produces="application/json">
<service name="createRestOrderExample"/>
+ <example type="response" code="200">
+ <![CDATA[
+ {
+ "statusCode": 200,
+ "statusDescription": "OK",
+ "data": {
+ "orderId": "DEMO-001"
+ }
+ }
+ ]]>
+ </example>
</operation>
</resource>
@@ -59,6 +70,19 @@ under the License.
</operation>
</resource>
+ <resource name="orders"
path="/{myInput}/testServiceInputParametersAsPath/">
+ <operation verb="get" consumes="application/json"
produces="application/json">
+ <service name="testServiceInputParameters"/>
+ </operation>
+ </resource>
+
+ <resource name="orders" path="/testServiceInputParametersAsQueryParam/">
+ <operation verb="get" consumes="application/json"
produces="application/json">
+ <service name="testServiceInputParameters"/>
+ <queryParam name="myInput" type="string" description="example
input" />
+ </operation>
+ </resource>
+
<mapping name="RestOrderExample"
className="org.apache.ofbiz.ws.rs.examples.RestOrderExample"/>
</api>
\ No newline at end of file
diff --git a/framework/rest-api/dtd/rest-api.xsd
b/framework/rest-api/dtd/rest-api.xsd
index 2c9f4fb740..de3fb320c8 100644
--- a/framework/rest-api/dtd/rest-api.xsd
+++ b/framework/rest-api/dtd/rest-api.xsd
@@ -53,6 +53,8 @@ under the License.
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" ref="service"/>
+ <xs:element minOccurs="0" maxOccurs="unbounded"
ref="queryParam"/>
+ <xs:element minOccurs="0" maxOccurs="unbounded" ref="example"/>
</xs:sequence>
<xs:attribute name="verb" use="required">
<xs:simpleType>
@@ -95,6 +97,41 @@ under the License.
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
+ <xs:element name="queryParam">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="description" use="optional"/>
+ <xs:attribute name="type" use="optional" default="string">
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="string"/>
+ <xs:enumeration value="number"/>
+ <xs:enumeration value="integer"/>
+ <xs:enumeration value="boolean"/>
+ <xs:enumeration value="array"/>
+ <xs:enumeration value="object"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="example">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="type" use="optional"
default="response">
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="response"/>
+ <xs:enumeration value="parameter"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="code" type="xs:string" use="required"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
<xs:element name="mapping">
<xs:complexType>
<xs:attribute name="name" type="xs:string" />
diff --git a/framework/rest-api/servicedef/services.xml
b/framework/rest-api/servicedef/services.xml
index 344835ff1d..0e786745ca 100644
--- a/framework/rest-api/servicedef/services.xml
+++ b/framework/rest-api/servicedef/services.xml
@@ -70,4 +70,11 @@ under the License.
<description>TestService that returns its locale</description>
<attribute name="localeAsString" type="String" mode="OUT"
optional="false"/>
</service>
+
+ <service name="testServiceInputParameters" engine="java"
+ location="org.apache.ofbiz.ws.rs.test.RestTestServices"
invoke="testServiceInputParameters">
+ <description>TestService that returns its locale</description>
+ <attribute name="myInput" type="String" mode="INOUT" optional="false"/>
+ </service>
+
</services>
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelApiReader.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelApiReader.java
index be74a9d882..a6aab6c294 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelApiReader.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelApiReader.java
@@ -133,6 +133,8 @@ public final class ModelApiReader {
.addApiResponses(UtilXml.checkEmpty(operationEle.getAttribute("addApiResponses")).intern())
.customHeaders(UtilXml.checkEmpty(operationEle.getAttribute("customHeaders"),
resourceEle.getAttribute("customHeaders")).intern());
+ createQueryParams(operationEle, op);
+ createExamples(operationEle, op);
resource.addOperation(op);
} else {
Debug.logWarning("Error during creation of ModelApi, due to
missing 'service' Attribute in ApiModelXml for"
@@ -141,4 +143,25 @@ public final class ModelApiReader {
}
}
+
+ private static void createQueryParams(Element operationEle, ModelOperation
operation) {
+ for (Element queryParamEle : UtilXml.childElementList(operationEle,
"queryParam")) {
+ ModelQueryParam qp = new ModelQueryParam()
+
.name(UtilXml.checkEmpty(queryParamEle.getAttribute("name")).intern())
+
.type(UtilXml.checkEmpty(queryParamEle.getAttribute("type")).intern())
+
.description(UtilXml.checkEmpty(queryParamEle.getAttribute("description")).intern());
+ operation.addQueryParam(qp);
+ }
+ }
+
+ private static void createExamples(Element operationEle, ModelOperation
operation) {
+ for (Element queryParamEle : UtilXml.childElementList(operationEle,
"example")) {
+ ModelExample example = new ModelExample()
+
.type(UtilXml.checkEmpty(queryParamEle.getAttribute("type")).intern())
+
.code(UtilXml.checkEmpty(queryParamEle.getAttribute("code")).intern())
+
.exampleText(UtilXml.checkEmpty(queryParamEle.getTextContent()).intern());
+ operation.addExample(example);
+ }
+ }
+
}
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelExample.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelExample.java
new file mode 100644
index 0000000000..f002515c00
--- /dev/null
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelExample.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * 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.ofbiz.ws.rs.model;
+
+
+public class ModelExample {
+
+ private String type;
+ private String code;
+ private String exampleText;
+
+ /**
+ * @return the type
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * @param type the type to set
+ */
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ /**
+ * @param type
+ * @return
+ */
+ public ModelExample type(String type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * @return the code
+ */
+ public String getCode() {
+ return code;
+ }
+
+ /**
+ * @param code the code to set
+ */
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ /**
+ * @param code
+ * @return
+ */
+ public ModelExample code(String code) {
+ this.code = code;
+ return this;
+ }
+
+
+ /**
+ * @return the exampleText
+ */
+ public String getExampleText() {
+ return exampleText;
+ }
+
+
+ /**
+ * @param exampleText the exampleText to set
+ */
+ public void setExampleText(String exampleText) {
+ this.exampleText = exampleText;
+ }
+
+ /**
+ * @param exampleText
+ * @return
+ */
+ public ModelExample exampleText(String exampleText) {
+ this.exampleText = exampleText;
+ return this;
+ }
+
+}
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelOperation.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelOperation.java
index 3c43732c7d..0bdaca5d20 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelOperation.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelOperation.java
@@ -18,11 +18,17 @@
*******************************************************************************/
package org.apache.ofbiz.ws.rs.model;
import java.util.ArrayList;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import org.apache.ofbiz.base.util.StringUtil;
+import org.apache.ofbiz.base.util.UtilGenerics;
import org.apache.ofbiz.base.util.UtilValidate;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
public class ModelOperation {
private String service;
@@ -36,6 +42,8 @@ public class ModelOperation {
private String mainAction;
private String addApiResponses;
private String customHeaders;
+ private List<ModelQueryParam> queryParams;
+ private List<ModelExample> examples;
/**
* Returns whether the user is authenticated.
@@ -377,11 +385,103 @@ public class ModelOperation {
return StringUtil.split(customHeaders, ",");
}
+ /**
+ * Retrieves queryParams if they are set or an empty ArrayList if not
+ *
+ * @return {@link #queryParams}
+ */
+ public List<ModelQueryParam> getQueryParams() {
+ return queryParams == null ? new ArrayList<ModelQueryParam>() :
queryParams;
+ }
+
+ /**
+ * Fluent-style setter for adding a queryParam, allowing method chaining
when building
+ * a {@link ModelOperation}.
+ * @param queryParam
+ * @return this {@link ModelOperation} instance
+ */
+ public ModelOperation addQueryParam(ModelQueryParam queryParam) {
+ if (this.queryParams == null) {
+ this.queryParams = new ArrayList<>();
+ }
+ this.queryParams.add(queryParam);
+ return this;
+ }
+
+ /**
+ * Returns the list of examples
+ * @return {@link List<ModelExample>}
+ */
+ public List<ModelExample> getExamples() {
+ return examples == null ? new ArrayList<ModelExample>() : examples;
+ }
+
+ /**
+ * Fluent-style setter for adding a {@link ModelExample}, allowing method
chaining when building
+ * a {@link ModelOperation}.
+ * @param example {@link ModelExample}
+ * @return {@link ModelOperation} instance
+ */
+ public ModelOperation addExample(ModelExample example) {
+ if (this.examples == null) {
+ this.examples = new ArrayList<>();
+ }
+ this.examples.add(example);
+ return this;
+ }
+
+ /**
+ * Retrieves an {@link ModelExample} based on type and code.
+ * Returns null if nothings found
+ * @param type
+ * @param code
+ * @return {@link ModelExample}
+ */
+ public ModelExample getExample(String type, String code) {
+ if (UtilValidate.isEmpty(type) || UtilValidate.isEmpty(code)) {
+ return null;
+ }
+ return getExamples().stream().filter(param ->
(type.equals(param.getType()) &&
code.equals(param.getCode()))).findFirst().orElse(null);
+
+ }
+
+ /**
+ * Tries to get a HashMap out of a JSON example text, preserving the order
of the entries.
+ * Returns a simple @String object if this fails.
+ * @param type
+ * @param code
+ * @return
+ */
+ public Object getExampleObject(String type, String code) {
+ ModelExample example = getExample(type, code);
+
+ if (example == null) {
+ return null;
+ }
+
+ String text = example.getExampleText();
+ if (text == null) {
+ return null;
+ }
+
+ Map<String, Object> obj = null;
+ try {
+ obj = UtilGenerics.cast(new ObjectMapper().readValue(text,
LinkedHashMap.class));
+ } catch (JsonProcessingException e) {
+ }
+
+ if (obj != null) {
+ return obj;
+ }
+ return new String(text);
+ }
+
@Override
public String toString() {
return "service: " + service + ", path: " + path + ", verb: " + verb +
", description: " + description
+ ", produces: " + produces + ", primaryPermission: " +
primaryPermission + ", mainAction: "
- + mainAction + ", addApiResponses:" + addApiResponses + ",
customHeaders: " + customHeaders;
+ + mainAction + ", addApiResponses:" + addApiResponses + ",
customHeaders: " + customHeaders
+ + ", queryParams: " + queryParams;
}
}
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/openapi/OFBizOpenApiReader.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/openapi/OFBizOpenApiReader.java
index 3c30c67f2f..4820b82b6a 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/openapi/OFBizOpenApiReader.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/openapi/OFBizOpenApiReader.java
@@ -25,6 +25,7 @@ import java.util.Map;
import java.util.Set;
import org.apache.ofbiz.base.util.Debug;
+import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.service.DispatchContext;
import org.apache.ofbiz.service.GenericServiceException;
import org.apache.ofbiz.service.LocalDispatcher;
@@ -37,6 +38,7 @@ import org.apache.ofbiz.ws.rs.listener.ApiContextListener;
import org.apache.ofbiz.ws.rs.model.ModelApi;
import org.apache.ofbiz.ws.rs.model.ModelMapping;
import org.apache.ofbiz.ws.rs.model.ModelOperation;
+import org.apache.ofbiz.ws.rs.model.ModelQueryParam;
import org.apache.ofbiz.ws.rs.model.ModelResource;
import org.apache.ofbiz.ws.rs.util.OpenApiUtil;
import org.apache.ofbiz.ws.rs.util.RestApiUtil;
@@ -157,24 +159,6 @@ public final class OFBizOpenApiReader extends Reader
implements OpenApiReader {
.deprecated(false)
.addSecurityItem(security);
- String verb = op.getVerb().toUpperCase();
- if (verb.equalsIgnoreCase(HttpMethod.GET)) {
- QueryParameter serviceInParam = (QueryParameter) new
QueryParameter().required(true)
- .description("Operation Input Parameters in
JSON").name("input");
-
- Schema<?> refSchema = new
Schema<>().$ref("#/components/schemas/api.request." + service.getName());
- serviceInParam.content(new
Content().addMediaType(jakarta.ws.rs.core.MediaType.APPLICATION_JSON,
- new MediaType().schema(refSchema)));
- operation.addParametersItem(serviceInParam);
- } else if (verb.matches(HttpMethod.POST + "|" + HttpMethod.PUT +
"|" + HttpMethod.PATCH)) {
- RequestBody request = new RequestBody()
- .description("Request Body for operation " +
op.getDescription())
- .content(new
Content().addMediaType(jakarta.ws.rs.core.MediaType.APPLICATION_JSON,
- new MediaType().schema(new
Schema<>().$ref("#/components/schemas/api.request." + service.getName()))));
- operation.setRequestBody(request);
- operation.addParametersItem(HEADER_CONTENT_TYPE_JSON);
- }
-
List<String> pathParams = RestApiUtil.getPathParameters(uri);
for (String pathParam : pathParams) {
ModelParam mdParam = service.getInModelParamList().stream()
@@ -187,10 +171,44 @@ public final class OFBizOpenApiReader extends Reader
implements OpenApiReader {
pathParameter.setSchema(OpenApiUtil.getAttributeSchema(service, mdParam));
operation.addParametersItem(pathParameter);
}
+ String verb = op.getVerb().toUpperCase();
+ if (verb.equalsIgnoreCase(HttpMethod.GET)) {
+ List<ModelQueryParam> queryParams = op.getQueryParams();
+
+ for (ModelQueryParam queryParam : queryParams) {
+ if (pathParams.contains(queryParam.getName())) {
+ Debug.logWarning("Query parameter '%s' for Service
'%s' is already defined as path parameter, ignoring.", MODULE,
+ queryParam.getName(), service.getName());
+ } else {
+ ModelParam mdParam =
service.getInModelParamList().stream().filter(param -> (
+ !param.getInternal() &&
queryParam.getName().equals(param.getName()))).findFirst().orElse(null);
+ if (mdParam != null) {
+ final QueryParameter serviceInParam =
(QueryParameter) new QueryParameter()
+ .required(!mdParam.isOptional())
+
.description(UtilValidate.isNotEmpty(queryParam.getDescription())
+ ? queryParam.getDescription() :
mdParam.getDescription())
+ .name(queryParam.getName())
+ .schema(new
Schema<>().type(queryParam.getType()));
+ operation.addParametersItem(serviceInParam);
+ } else {
+ Debug.logWarning("Query parameter '%s' for Service
'%s' not found in service definition, ignoring.", MODULE,
+ queryParam.getName(), service.getName());
+ }
+ }
+ }
+ } else if (verb.matches(HttpMethod.POST + "|" + HttpMethod.PUT +
"|" + HttpMethod.PATCH)) {
+ RequestBody request = new RequestBody()
+ .description("Request Body for operation " +
op.getDescription())
+ .content(new
Content().addMediaType(jakarta.ws.rs.core.MediaType.APPLICATION_JSON,
+ new MediaType().schema(new
Schema<>().$ref("#/components/schemas/api.request." + service.getName()))));
+ operation.setRequestBody(request);
+ operation.addParametersItem(HEADER_CONTENT_TYPE_JSON);
+ }
+
addServiceOutSchema(service);
addServiceInSchema(service, op);
- addServiceOperationApiResponses(service, operation);
+ addServiceOperationApiResponses(service, op, operation);
addAdditionalOperationApiResponses(service, op, operation);
addCustomHeaders(op, operation);
setPathItemOperation(pathItemObject, verb.toUpperCase(),
operation);
@@ -247,7 +265,7 @@ public final class OFBizOpenApiReader extends Reader
implements OpenApiReader {
addServiceOutSchema(service);
addServiceInSchema(service);
- addServiceOperationApiResponses(service, operation);
+ addServiceOperationApiResponses(service, null, operation);
setPathItemOperation(pathItemObject, HttpMethod.POST,
operation);
paths.addPathItem("/services/" + service.getName(),
pathItemObject);
}
@@ -320,9 +338,9 @@ public final class OFBizOpenApiReader extends Reader
implements OpenApiReader {
});
}
- private void addServiceOperationApiResponses(ModelService service,
Operation operation) {
+ private void addServiceOperationApiResponses(ModelService service,
ModelOperation op, Operation operation) {
ApiResponses apiResponsesObject = new ApiResponses();
- ApiResponse successResponse =
OpenApiUtil.buildSuccessResponse(service);
+ ApiResponse successResponse =
OpenApiUtil.buildSuccessResponse(service, op);
apiResponsesObject.addApiResponse(String.valueOf(Response.Status.OK.getStatusCode()),
successResponse);
OpenApiUtil.getStandardApiResponses().forEach((code, response) -> {
apiResponsesObject.addApiResponse(code, response);
@@ -359,9 +377,12 @@ public final class OFBizOpenApiReader extends Reader
implements OpenApiReader {
} else {
schemaName =
"api.response.service.".concat(service.getName()).concat(".").concat(String.valueOf(statusType.getStatusCode()));
- ApiResponse response = new
ApiResponse().description(statusType.getReasonPhrase()).content(new
Content().addMediaType(
- javax.ws.rs.core.MediaType.APPLICATION_JSON,
- new MediaType().schema(new
Schema<>().$ref("#/components/schemas/" + schemaName))));
+ ApiResponse response = new ApiResponse()
+ .description(statusType.getReasonPhrase())
+ .content(new Content()
+
.addMediaType(javax.ws.rs.core.MediaType.APPLICATION_JSON, new MediaType()
+ .schema(new
Schema<>().$ref("#/components/schemas/" + schemaName))
+
.example(op.getExampleObject("response",
String.valueOf(statusType.getStatusCode())))));
apiResponsesObjectCopy.addApiResponse(statusCode,
response);
}
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/OpenApiUtil.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/OpenApiUtil.java
index 69329bbb1b..627a8515c7 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/OpenApiUtil.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/OpenApiUtil.java
@@ -361,7 +361,7 @@ public final class OpenApiUtil {
}
Schema<?> attrSchema = getAttributeSchema(service, param);
if (attrSchema != null) {
- parentSchema.addProperty(name, getAttributeSchema(service,
service.getParam(name)));
+ parentSchema.addProperty(name, attrSchema);
}
}
});
@@ -517,6 +517,34 @@ public final class OpenApiUtil {
}
try {
schema = (Schema<?>)
schemaClass.getDeclaredConstructor().newInstance();
+ //@Schema(minLength = 1, maxLength = 20, nullable = false,
example="10001", description = "Interne Party ID")
+ io.swagger.v3.oas.annotations.media.Schema fieldAnnotation
=
+
field.getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class);
+ if (fieldAnnotation != null) {
+ if (UtilValidate.isNotEmpty(fieldAnnotation.name())) {
+ schema.setName(fieldAnnotation.name());
+ }
+ schema.setDescription(fieldAnnotation.description() !=
null ? fieldAnnotation.description() : fieldNm);
+ if
(UtilValidate.isNotEmpty(fieldAnnotation.example())) {
+ schema.setExample(fieldAnnotation.example());
+ }
+ if
(UtilValidate.isNotEmpty(fieldAnnotation.minLength())) {
+ schema.setMinLength(fieldAnnotation.minLength());
+ }
+ if
(UtilValidate.isNotEmpty(fieldAnnotation.maxLength())) {
+ schema.setMaxLength(fieldAnnotation.maxLength());
+ }
+ if
(UtilValidate.isNotEmpty(fieldAnnotation.defaultValue())) {
+ schema.setDefault(fieldAnnotation.defaultValue());
+ }
+ if (UtilValidate.isNotEmpty(fieldAnnotation.format()))
{
+ schema.setFormat(fieldAnnotation.format());
+ }
+ if
(UtilValidate.isNotEmpty(fieldAnnotation.nullable())) {
+ schema.setNullable(fieldAnnotation.nullable());
+ }
+ }
+
if (schema instanceof ArraySchema) {
ParameterizedType genericType = (ParameterizedType)
field.getGenericType();
Class<? extends Type> genericClass = (Class<? extends
Type>) genericType.getActualTypeArguments()[0];
@@ -540,11 +568,11 @@ public final class OpenApiUtil {
}
} catch (InstantiationException | IllegalAccessException |
NoSuchMethodException
| InvocationTargetException e) {
- e.printStackTrace();
+ Debug.logError(e, "Error evaluating class [%s].", MODULE,
className);
}
}
} catch (ClassNotFoundException e1) {
- e1.printStackTrace();
+ Debug.logError(e1, "Error evaluating class [%s].", MODULE,
className);
}
return dataSchema;
}
@@ -591,15 +619,21 @@ public final class OpenApiUtil {
* OpenAPI response schema for the given service.
*
* @param service the {@link ModelService} for which to build the success
response
+ * @param op {@link ModelOperation}
* @return an {@link ApiResponse} with a JSON media type and a schema
reference
* to {@code
#/components/schemas/api.response.<serviceName>.success}
*/
- public static ApiResponse buildSuccessResponse(ModelService service) {
- final ApiResponse success = new ApiResponse()
+ public static ApiResponse buildSuccessResponse(ModelService service,
ModelOperation op) {
+ final MediaType mediaType = new MediaType()
+ .schema(new Schema<>().$ref("#/components/schemas/" +
"api.response." + service.getName() + ".success"));
+
+ if (op != null) {
+ mediaType.setExample(op.getExampleObject("response", "200"));
+ }
+
+ return new ApiResponse()
.description("Success response for the API call.")
.content(new Content()
-
.addMediaType(jakarta.ws.rs.core.MediaType.APPLICATION_JSON, new MediaType()
- .schema(new
Schema<>().$ref("#/components/schemas/" + "api.response." + service.getName() +
".success"))));
- return success;
+
.addMediaType(javax.ws.rs.core.MediaType.APPLICATION_JSON, mediaType));
}
}
diff --git
a/framework/rest-api/src/test/java/org/apache/ofbiz/ws/rs/test/RestTestHttpRequest.java
b/framework/rest-api/src/test/java/org/apache/ofbiz/ws/rs/test/RestTestHttpRequest.java
index 000a2539fc..29e5df6176 100644
---
a/framework/rest-api/src/test/java/org/apache/ofbiz/ws/rs/test/RestTestHttpRequest.java
+++
b/framework/rest-api/src/test/java/org/apache/ofbiz/ws/rs/test/RestTestHttpRequest.java
@@ -172,4 +172,55 @@ class RestTestHttpRequest implements JupiterTestHelper {
assertEquals("fr", localeSentViaAcceptLanguageHeader);
}
+
+ @Test
+ void testServiceInputParametersAsPath() throws Exception {
+ HttpClient client = initHttpClient();
+ client.setHeader("Content-Type", "application/json");
+ client.setHeader("Authorization", "Bearer " + accessToken);
+ client.setHeader("Accept-Language", "fr");
+ client.setUrl(BASE_URL +
"/exampleApi/foo/testServiceInputParametersAsPath/");
+
+ String response = "";
+ try {
+ response = client.get();
+ } catch (HttpClientException e) {
+ Debug.logError(e, "Error during rest GET to
/exampleApi/foo/testServiceInputParametersAsPath/", MODULE);
+ }
+ String serviceInputParameter = "";
+ try {
+ JsonNode root = mapper.readTree(response);
+ serviceInputParameter = root.get("data").get("myInput").asText();
+ } catch (JsonProcessingException | NullPointerException e) {
+ Debug.logError(e, "Error parsing rest auth response", MODULE);
+ }
+
+ assertEquals("foo", serviceInputParameter);
+ }
+
+ @Test
+ void testServiceInputParametersAsQueryParam() throws Exception {
+ HttpClient client = initHttpClient();
+ client.setHeader("Content-Type", "application/json");
+ client.setHeader("Authorization", "Bearer " + accessToken);
+ client.setHeader("Accept-Language", "fr");
+ client.setUrl(BASE_URL +
"/exampleApi/testServiceInputParametersAsQueryParam");
+ client.setParameter("myInput", "foo");
+
+ String response = "";
+ try {
+ response = client.get();
+ } catch (HttpClientException e) {
+ Debug.logError(e, "Error during rest GET to
/exampleApi/testServiceInputParametersAsQueryParam", MODULE);
+ }
+ String serviceInputParameter = "";
+ try {
+ JsonNode root = mapper.readTree(response);
+ serviceInputParameter = root.get("data").get("myInput").asText();
+ } catch (JsonProcessingException | NullPointerException e) {
+ Debug.logError(e, "Error parsing rest auth response", MODULE);
+ }
+
+ assertEquals("foo", serviceInputParameter);
+ }
}
diff --git
a/framework/rest-api/src/test/java/org/apache/ofbiz/ws/rs/test/RestTestServices.java
b/framework/rest-api/src/test/java/org/apache/ofbiz/ws/rs/test/RestTestServices.java
index 3c1e8c8077..be0cca3b11 100644
---
a/framework/rest-api/src/test/java/org/apache/ofbiz/ws/rs/test/RestTestServices.java
+++
b/framework/rest-api/src/test/java/org/apache/ofbiz/ws/rs/test/RestTestServices.java
@@ -21,6 +21,7 @@ package org.apache.ofbiz.ws.rs.test;
import java.util.Locale;
import java.util.Map;
+import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.service.DispatchContext;
import org.apache.ofbiz.service.ModelService;
@@ -99,4 +100,11 @@ public class RestTestServices {
result.put("localeAsString", localeAsString);
return result;
}
+
+ public static Map<String, Object>
testServiceInputParameters(DispatchContext dctx, Map<String, ? extends Object>
context) {
+ Map<String, Object> result = ServiceUtil.returnSuccess();
+ Debug.logInfo("My value" + (String) context.get("myInput"), null);
+ result.put("myInput", (String) context.get("myInput"));
+ return result;
+ }
}
diff --git
a/framework/service/src/main/java/org/apache/ofbiz/service/ModelParam.java
b/framework/service/src/main/java/org/apache/ofbiz/service/ModelParam.java
index 28dc20e06a..3c37f8f902 100644
--- a/framework/service/src/main/java/org/apache/ofbiz/service/ModelParam.java
+++ b/framework/service/src/main/java/org/apache/ofbiz/service/ModelParam.java
@@ -219,6 +219,14 @@ public class ModelParam implements Serializable {
this.description = description;
}
+ /**
+ * Returns the description
+ * @return the description
+ */
+ public String getDescription() {
+ return description;
+ }
+
/**
* Sets override optional.
* @param overrideOptional the override optional