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

Reply via email to