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 2890723b60 Implemented: Retrieval of custom httpHeaders and use as
service IN parameters (OFBIZ-12515)
2890723b60 is described below
commit 2890723b608a57aacbd3c4b66e2e216cc3a30a71
Author: Lukas Finster <[email protected]>
AuthorDate: Tue Aug 11 16:18:19 2026 +0200
Implemented: Retrieval of custom httpHeaders and use as service IN
parameters (OFBIZ-12515)
---
.../rest-api/api/exampleApiDefinition.rest.xml | 6 +++
framework/rest-api/dtd/rest-api.xsd | 2 +
framework/rest-api/servicedef/services.xml | 6 +++
.../apache/ofbiz/ws/rs/model/ModelApiReader.java | 7 ++-
.../apache/ofbiz/ws/rs/model/ModelOperation.java | 48 ++++++++++++++++++++-
.../apache/ofbiz/ws/rs/model/ModelResource.java | 50 +++++++++++++++++++++-
.../ofbiz/ws/rs/openapi/OFBizOpenApiReader.java | 6 +++
.../ofbiz/ws/rs/process/RestRequestHandler.java | 15 +++++++
.../ofbiz/ws/rs/test/RestTestHttpRequest.java | 39 ++++++++++++++---
.../apache/ofbiz/ws/rs/test/RestTestServices.java | 18 ++++++++
10 files changed, 186 insertions(+), 11 deletions(-)
diff --git a/framework/rest-api/api/exampleApiDefinition.rest.xml
b/framework/rest-api/api/exampleApiDefinition.rest.xml
index 137f56de4f..dc110db901 100644
--- a/framework/rest-api/api/exampleApiDefinition.rest.xml
+++ b/framework/rest-api/api/exampleApiDefinition.rest.xml
@@ -47,6 +47,12 @@ under the License.
</operation>
</resource>
+ <resource name="orders" path="/useCustomHeaderAsServiceParameter">
+ <operation verb="post" consumes="application/json"
produces="application/json" customHeaders="x-custom-header">
+ <service name="useCustomHeaderAsServiceParameter"/>
+ </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 f193cff990..8c2fbd7938 100644
--- a/framework/rest-api/dtd/rest-api.xsd
+++ b/framework/rest-api/dtd/rest-api.xsd
@@ -44,6 +44,7 @@ under the License.
<xs:attribute name="description" type="xs:string"/>
<xs:attribute name="publish" type="xs:boolean" default="true"/>
<xs:attribute name="auth" type="xs:boolean" default="true"/>
+ <xs:attribute name="customHeaders" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:element name="operation">
@@ -82,6 +83,7 @@ under the License.
<xs:attribute name="description" type="xs:string"/>
<xs:attribute name="auth" type="xs:boolean" default="true"/>
<xs:attribute name="addApiResponses" type="xs:string"/>
+ <xs:attribute name="customHeaders" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:element name="service">
diff --git a/framework/rest-api/servicedef/services.xml
b/framework/rest-api/servicedef/services.xml
index ba0eb06b60..e83b3966ee 100644
--- a/framework/rest-api/servicedef/services.xml
+++ b/framework/rest-api/servicedef/services.xml
@@ -59,4 +59,10 @@ under the License.
<attribute name="httpResponseStatus" type="java.lang.Integer"
mode="OUT" optional="false"/>
</service>
+ <service name="useCustomHeaderAsServiceParameter" engine="java"
+ location="org.apache.ofbiz.ws.rs.test.RestTestServices"
invoke="useCustomHeaderAsServiceParameter">
+ <description>TestService that returns success, but overwrites it with
a custom http status code</description>
+ <attribute name="x-custom-header" type="String" mode="INOUT"
optional="true"/>
+ </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 72f5c3a1be..f8c7ef17b3 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
@@ -108,7 +108,8 @@ public final class ModelApiReader {
.displayName(UtilXml.checkEmpty(resourceEle.getAttribute("displayName")).intern())
.path(UtilXml.checkEmpty(resourceEle.getAttribute("path")).intern())
.publish(Boolean.parseBoolean(UtilXml.checkEmpty(resourceEle.getAttribute("publish")).intern()))
-
.auth(Boolean.parseBoolean(UtilXml.checkEmpty(resourceEle.getAttribute("auth")).intern()));
+
.auth(Boolean.parseBoolean(UtilXml.checkEmpty(resourceEle.getAttribute("auth")).intern()))
+
.customHeaders(UtilXml.checkEmpty(resourceEle.getAttribute("customHeaders")).intern());
}
private static void createOperations(Element resourceEle, ModelResource
resource) {
@@ -123,7 +124,9 @@ public final class ModelApiReader {
.consumes(UtilXml.checkEmpty(operationEle.getAttribute("consumes")).intern())
.description(UtilXml.checkEmpty(operationEle.getAttribute("description")).intern())
.auth(Boolean.parseBoolean(UtilXml.checkEmpty(operationEle.getAttribute("auth")).intern()))
-
.addApiResponses(UtilXml.checkEmpty(operationEle.getAttribute("addApiResponses")).intern());
+
.addApiResponses(UtilXml.checkEmpty(operationEle.getAttribute("addApiResponses")).intern())
+
.customHeaders(UtilXml.checkEmpty(operationEle.getAttribute("customHeaders"),
+
resourceEle.getAttribute("customHeaders")).intern());
resource.addOperation(op);
} else {
Debug.logWarning("Error during creation of ModelApi, due to
missing 'service' Attribute in ApiModelXml for"
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 995c52a71b..cdc15f8f41 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
@@ -33,6 +33,7 @@ public class ModelOperation {
private String description;
private boolean auth;
private String addApiResponses;
+ private String customHeaders;
/**
* Returns whether the user is authenticated.
@@ -271,10 +272,55 @@ public class ModelOperation {
return this;
}
+ /**
+ * Gets the raw, comma-separated custom headers string configured for this
resource.
+ *
+ * @return the customHeaders as a raw comma-separated string, or {@code
null} if unset
+ */
+ public String getCustomHeaders() {
+ return customHeaders;
+ }
+
+ /**
+ * Sets the raw custom headers for this resource, as a comma-separated
string.
+ *
+ * @param customHeaders the customHeaders to set, expected as a
comma-separated list
+ */
+ public void setCustomHeaders(String customHeaders) {
+ this.customHeaders = customHeaders;
+ }
+
+ /**
+ * Fluent-style setter for customHeaders, allowing method chaining when
building
+ * a {@link ModelOperation}.
+ *
+ * @param customHeaders the comma-separated custom headers to set
+ * @return this {@link ModelOperation} instance, for chaining
+ */
+ public ModelOperation customHeaders(String customHeaders) {
+ this.customHeaders = customHeaders;
+ return this;
+ }
+
+ /**
+ * Parses the raw {@link #customHeaders} string into a list of individual
header names,
+ * splitting on commas.
+ *
+ * @return a {@link List} of individual custom header names; an empty list
if
+ * {@link #customHeaders} is unset or empty
+ */
+ public List<String> getCustomHeadersList() {
+ if (UtilValidate.isEmpty(customHeaders)) {
+ return new ArrayList<>();
+ }
+ return StringUtil.split(customHeaders, ",");
+ }
+
@Override
public String toString() {
return "service: " + service + ", path: " + path + ", verb: " + verb +
", description: " + description
- + ", produces: " + produces + ", addApiResponses:" +
addApiResponses;
+ + ", produces: " + produces + ", addApiResponses:" +
addApiResponses + ", customHeaders: "
+ + customHeaders;
}
}
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelResource.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelResource.java
index 624599e226..2f01ffbf56 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelResource.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/model/ModelResource.java
@@ -21,6 +21,9 @@ package org.apache.ofbiz.ws.rs.model;
import java.util.ArrayList;
import java.util.List;
+import org.apache.ofbiz.base.util.StringUtil;
+import org.apache.ofbiz.base.util.UtilValidate;
+
public class ModelResource {
private List<ModelOperation> operations;
@@ -31,6 +34,7 @@ public class ModelResource {
private String description;
private boolean publish;
private boolean auth;
+ private String customHeaders;
/**
* Returns whether the user is authenticated.
@@ -255,11 +259,55 @@ public class ModelResource {
return this;
}
+ /**
+ * Gets the raw, comma-separated custom headers string configured for this
resource.
+ *
+ * @return the customHeaders as a raw comma-separated string, or {@code
null} if unset
+ */
+ public String getCustomHeaders() {
+ return customHeaders;
+ }
+
+ /**
+ * Sets the raw custom headers for this resource, as a comma-separated
string.
+ *
+ * @param customHeaders the customHeaders to set, expected as a
comma-separated list
+ */
+ public void setCustomHeaders(String customHeaders) {
+ this.customHeaders = customHeaders;
+ }
+
+ /**
+ * Fluent-style setter for customHeaders, allowing method chaining when
building
+ * a {@link ModelResource}.
+ *
+ * @param customHeaders the comma-separated custom headers to set
+ * @return this {@link ModelResource} instance, for chaining
+ */
+ public ModelResource customHeaders(String customHeaders) {
+ this.customHeaders = customHeaders;
+ return this;
+ }
+
+ /**
+ * Parses the raw {@link #customHeaders} string into a list of individual
header names,
+ * splitting on commas.
+ *
+ * @return a {@link List} of individual custom header names; an empty list
if
+ * {@link #customHeaders} is unset or empty
+ */
+ public List<String> getCustomHeadersList() {
+ if (UtilValidate.isEmpty(customHeaders)) {
+ return new ArrayList<>();
+ }
+ return StringUtil.split(customHeaders, ",");
+ }
+
@Override
public String toString() {
// TODO Auto-generated method stub
return "name: " + name + ", path: " + path + ", displayName: " +
displayName + ", description: " + description
- + ", publish: " + publish;
+ + ", publish: " + publish + ", customHeaders: " +
customHeaders;
}
}
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 6c45403d7f..3c30c67f2f 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
@@ -192,6 +192,7 @@ public final class OFBizOpenApiReader extends Reader
implements OpenApiReader {
addServiceInSchema(service, op);
addServiceOperationApiResponses(service, operation);
addAdditionalOperationApiResponses(service, op, operation);
+ addCustomHeaders(op, operation);
setPathItemOperation(pathItemObject, verb.toUpperCase(),
operation);
if (!pathExists) {
@@ -374,4 +375,9 @@ public final class OFBizOpenApiReader extends Reader
implements OpenApiReader {
});
}
+ private void addCustomHeaders(ModelOperation op, Operation operation) {
+ op.getCustomHeadersList().forEach((headerName) -> {
+ operation.addParametersItem(new
HeaderParameter().name(headerName).schema(new StringSchema()).required(true));
+ });
+ }
}
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/process/RestRequestHandler.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/process/RestRequestHandler.java
index d1d205bc18..41e590c8f9 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/process/RestRequestHandler.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/process/RestRequestHandler.java
@@ -212,6 +212,16 @@ public abstract class RestRequestHandler implements
Inflector<ContainerRequestCo
return extract(requestContext.getUriInfo().getQueryParameters());
}
+ /**
+ * Extracts the header Parameters
+ *
+ * @param requestContext the request context
+ * @return a map containing headers
+ */
+ protected Map<String, Object>
extractHeaderParameters(ContainerRequestContext requestContext) {
+ return extract(requestContext.getHeaders());
+ }
+
/**
* Converts a multivalued parameter map into a standard argument map.
*
@@ -248,6 +258,7 @@ public abstract class RestRequestHandler implements
Inflector<ContainerRequestCo
Map<String, Object> arguments = new HashMap<>();
arguments.putAll(extractPathParameters(requestContext));
arguments.putAll(extractQueryParameters(requestContext));
+ arguments.putAll(extractHeaderParameters(requestContext));
return execute(requestContext, arguments);
}
@@ -256,6 +267,7 @@ public abstract class RestRequestHandler implements
Inflector<ContainerRequestCo
arguments.putAll(extractRequestBody(requestContext));
arguments.putAll(extractPathParameters(requestContext));
arguments.putAll(extractQueryParameters(requestContext));
+ arguments.putAll(extractHeaderParameters(requestContext));
return execute(requestContext, arguments);
}
@@ -264,6 +276,7 @@ public abstract class RestRequestHandler implements
Inflector<ContainerRequestCo
arguments.putAll(extractRequestBody(requestContext));
arguments.putAll(extractPathParameters(requestContext));
arguments.putAll(extractQueryParameters(requestContext));
+ arguments.putAll(extractHeaderParameters(requestContext));
return execute(requestContext, arguments);
}
@@ -272,6 +285,7 @@ public abstract class RestRequestHandler implements
Inflector<ContainerRequestCo
arguments.putAll(extractRequestBody(requestContext));
arguments.putAll(extractPathParameters(requestContext));
arguments.putAll(extractQueryParameters(requestContext));
+ arguments.putAll(extractHeaderParameters(requestContext));
return execute(requestContext, arguments);
}
@@ -279,6 +293,7 @@ public abstract class RestRequestHandler implements
Inflector<ContainerRequestCo
Map<String, Object> arguments = new HashMap<>();
arguments.putAll(extractPathParameters(requestContext));
arguments.putAll(extractQueryParameters(requestContext));
+ arguments.putAll(extractHeaderParameters(requestContext));
return execute(requestContext, arguments);
}
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 9a1c071717..85e5c230da 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
@@ -62,7 +62,7 @@ class RestTestHttpRequest implements JupiterTestHelper {
try {
response = fetchClient.post();
} catch (HttpClientException e) {
- Debug.logError(e, "Error returning rest access token", "MODULE");
+ Debug.logError(e, "Error returning rest access token", MODULE);
return;
}
@@ -70,7 +70,7 @@ class RestTestHttpRequest implements JupiterTestHelper {
JsonNode root = mapper.readTree(response);
accessToken = root.get("data").get("access_token").asText();
} catch (JsonProcessingException | NullPointerException e) {
- Debug.logError(e, "Error parsing rest auth response", "MODULE");
+ Debug.logError(e, "Error parsing rest auth response", MODULE);
}
}
@@ -85,14 +85,14 @@ class RestTestHttpRequest implements JupiterTestHelper {
try {
response = client.post();
} catch (HttpClientException e) {
- Debug.logError(e, "Error returning rest access token", "MODULE");
+ Debug.logError(e, "Error during rest POST to
/exampleApi/returnSuccess", MODULE);
}
int statusCode = 0;
try {
JsonNode root = mapper.readTree(response);
statusCode = root.get("statusCode").asInt(999999999);
} catch (JsonProcessingException | NullPointerException e) {
- Debug.logError(e, "Error parsing rest auth response", "MODULE");
+ Debug.logError(e, "Error parsing rest auth response", MODULE);
}
assertEquals(200, statusCode);
@@ -104,22 +104,47 @@ class RestTestHttpRequest implements JupiterTestHelper {
HttpClient client = initHttpClient();
client.setHeader("Content-Type", "application/json");
client.setHeader("Authorization", "Bearer " + accessToken);
- client.setUrl(BASE_URL +
"/exampleApi//returnSuccessButOverwriteStatusCode");
+ client.setUrl(BASE_URL +
"/exampleApi/returnSuccessButOverwriteStatusCode");
String response = "";
try {
response = client.post();
} catch (HttpClientException e) {
- Debug.logError(e, "Error returning rest access token", "AAAA");
+ Debug.logError(e, "Error during rest POST to
/exampleApi/returnSuccessButOverwriteStatusCode", MODULE);
}
int statusCode = 0;
try {
JsonNode root = mapper.readTree(response);
statusCode = root.get("statusCode").asInt(999999999);
} catch (JsonProcessingException | NullPointerException e) {
- Debug.logError(e, "Error parsing rest auth response", "MODULE");
+ Debug.logError(e, "Error parsing rest auth response", MODULE);
}
assertEquals(201, statusCode);
}
+
+ @Test
+ void useCustomHeaderAsServiceParameter() throws Exception {
+ HttpClient client = initHttpClient();
+ client.setHeader("Content-Type", "application/json");
+ client.setHeader("Authorization", "Bearer " + accessToken);
+ client.setHeader("x-custom-header", "Foo");
+ client.setUrl(BASE_URL +
"/exampleApi/useCustomHeaderAsServiceParameter");
+
+ String response = "";
+ try {
+ response = client.post();
+ } catch (HttpClientException e) {
+ Debug.logError(e, "Error during rest POST to
/exampleApi/useCustomHeaderAsServiceParameter", MODULE);
+ }
+ String customHeaderValue = "";
+ try {
+ JsonNode root = mapper.readTree(response);
+ customHeaderValue =
root.get("data").get("x-custom-header").asText();
+ } catch (JsonProcessingException | NullPointerException e) {
+ Debug.logError(e, "Error parsing rest auth response", MODULE);
+ }
+
+ assertEquals("Foo", customHeaderValue);
+ }
}
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 9fa10f7d7f..8fe50fd0cd 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
@@ -20,6 +20,7 @@ package org.apache.ofbiz.ws.rs.test;
import java.util.Map;
+import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.service.DispatchContext;
import org.apache.ofbiz.service.ModelService;
import org.apache.ofbiz.service.ServiceUtil;
@@ -65,4 +66,21 @@ public class RestTestServices {
result.put(RestApiUtil.RESPONSE_STATUS_KEY, 201);
return result;
}
+
+ /**
+ * TestService requiring a customHeader 'x-custom-header' to be present
+ *
+ * @param dctx
+ * @param context
+ * @return result
+ */
+ public static Map<String, Object>
useCustomHeaderAsServiceParameter(DispatchContext dctx, Map<String, ? extends
Object> context) {
+ String customHeader = (String) context.get("x-custom-header");
+ if (UtilValidate.isEmpty(customHeader)) {
+ return ServiceUtil.returnError("Missing custom header
'x-custom-header'");
+ }
+ Map<String, Object> result = ServiceUtil.returnSuccess();
+ result.put("x-custom-header", customHeader);
+ return result;
+ }
}