This is an automated email from the ASF dual-hosted git repository.

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new 546dc444a0 Webservice improvements, fixes #5425 ,#4692, #4277 and 
#2673  (#5795)
546dc444a0 is described below

commit 546dc444a020ff77151dcc19d1fa327671788b63
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Wed Oct 8 16:04:39 2025 +0200

    Webservice improvements, fixes #5425 ,#4692, #4277 and #2673  (#5795)
    
    * Add option to change statuscode in webservice, fixes #4692
    
    * Add option to include header field in pipeline, fixes #4277
    
    * Add support for binary fields in web service, fixes #5425 fixes #2673
---
 .../modules/ROOT/pages/hop-server/web-service.adoc |   8 +-
 .../java/org/apache/hop/www/WebServiceServlet.java |  55 +++++++-
 .../org/apache/hop/www/service/WebService.java     | 144 ++-------------------
 .../hop/ui/www/service/WebServiceEditor.java       |  52 ++++++++
 .../hop/ui/www/service/WebServiceGuiPlugin.java    |   2 +
 .../www/service/messages/messages_en_US.properties |   3 +
 6 files changed, 126 insertions(+), 138 deletions(-)

diff --git 
a/docs/hop-user-manual/modules/ROOT/pages/hop-server/web-service.adoc 
b/docs/hop-user-manual/modules/ROOT/pages/hop-server/web-service.adoc
index bcf377f536..4c741ea882 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/hop-server/web-service.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/hop-server/web-service.adoc
@@ -54,6 +54,9 @@ Make sure that the pipeline you want to execute is available 
on the server.
 |Output field
 |The output field from which this service will take data from, convert it to a 
String and output it
 
+|Status field
+|The field that contains the status code to be returned by the service, 
default status code is `200` if left empty
+
 |Content type
 |The content type which will get reported by the webService servlet
 
@@ -61,7 +64,10 @@ Make sure that the pipeline you want to execute is available 
on the server.
 |Enable this option if you want the executions of the web service pipeline to 
be listed in the status of the server.
 
 |Request body content variable
-|This is the name of the variable which at runtime will contain the content of 
the request body content.  This is useful when doing a POST against the 
webservice.
+|This is the name of the variable which at runtime will contain the content of 
the request body.  This is useful when doing a POST against the webservice.
+
+|Request header content variable
+|This is the name of the variable which at runtime will contain the content of 
the request header.  This is return a json object containing all headers that 
were in the request.
 
 |===
 
diff --git a/engine/src/main/java/org/apache/hop/www/WebServiceServlet.java 
b/engine/src/main/java/org/apache/hop/www/WebServiceServlet.java
index ed5810a393..3dcc515477 100644
--- a/engine/src/main/java/org/apache/hop/www/WebServiceServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/WebServiceServlet.java
@@ -17,10 +17,13 @@
 
 package org.apache.hop.www;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
+import java.util.Enumeration;
 import java.util.UUID;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
@@ -36,6 +39,7 @@ import org.apache.hop.core.logging.LoggingObjectType;
 import org.apache.hop.core.logging.SimpleLoggingObject;
 import org.apache.hop.core.metadata.SerializableMetadataProvider;
 import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.i18n.BaseMessages;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
@@ -120,7 +124,9 @@ public class WebServiceServlet extends BaseHttpServlet 
implements IHopServerPlug
       String transformName = variables.resolve(webService.getTransformName());
       String fieldName = variables.resolve(webService.getFieldName());
       String contentType = variables.resolve(webService.getContentType());
+      String statusCodeField = variables.resolve(webService.getStatusCode());
       String bodyContentVariable = 
variables.resolve(webService.getBodyContentVariable());
+      String headerContentVariable = 
variables.resolve(webService.getHeaderContentVariable());
 
       String bodyContent = "";
       if (StringUtils.isNotEmpty(bodyContentVariable)) {
@@ -129,6 +135,22 @@ public class WebServiceServlet extends BaseHttpServlet 
implements IHopServerPlug
         bodyContent = out.toString(StandardCharsets.UTF_8);
       }
 
+      String headerContent = "";
+      if (StringUtils.isNotEmpty(headerContentVariable)) {
+        // Create JSON object containing all request headers
+        ObjectMapper objectMapper = new ObjectMapper();
+        ObjectNode headersJson = objectMapper.createObjectNode();
+
+        Enumeration<String> headerNames = request.getHeaderNames();
+        while (headerNames.hasMoreElements()) {
+          String headerName = headerNames.nextElement();
+          String headerValue = request.getHeader(headerName);
+          headersJson.put(headerName, headerValue);
+        }
+
+        headerContent = objectMapper.writeValueAsString(headersJson);
+      }
+
       if (StringUtils.isEmpty(contentType)) {
         response.setContentType("text/plain");
       } else {
@@ -159,6 +181,10 @@ public class WebServiceServlet extends BaseHttpServlet 
implements IHopServerPlug
         pipeline.setVariable(bodyContentVariable, Const.NVL(bodyContent, ""));
       }
 
+      if (StringUtils.isNotEmpty(headerContentVariable)) {
+        pipeline.setVariable(headerContentVariable, Const.NVL(headerContent, 
""));
+      }
+
       // Set all the other parameters as variables/parameters...
       //
       String[] pipelineParameters = pipelineMeta.listParameters();
@@ -205,8 +231,31 @@ public class WebServiceServlet extends BaseHttpServlet 
implements IHopServerPlug
             public void rowWrittenEvent(IRowMeta rowMeta, Object[] row)
                 throws HopTransformException {
               try {
-                String outputString = rowMeta.getString(row, fieldName, "");
-                
outputStream.write(outputString.getBytes(StandardCharsets.UTF_8));
+                response.setStatus(rowMeta.getInteger(row, statusCodeField, 
200L).intValue());
+
+                // Get the field index and metadata to detect field type
+                int fieldIndex = rowMeta.indexOfValue(fieldName);
+                if (fieldIndex < 0) {
+                  throw new HopTransformException("Field '" + fieldName + "' 
not found in row");
+                }
+
+                IValueMeta valueMeta = rowMeta.getValueMeta(fieldIndex);
+
+                // Check if field is binary type and handle accordingly
+                byte[] outputData;
+                if (valueMeta.getType() == IValueMeta.TYPE_BINARY) {
+                  // Binary output - get raw bytes without encoding conversion
+                  outputData = rowMeta.getBinary(row, fieldIndex);
+                  if (outputData == null) {
+                    outputData = new byte[0];
+                  }
+                } else {
+                  // Text output - convert to string and encode as UTF-8
+                  String outputString = rowMeta.getString(row, fieldName, "");
+                  outputData = outputString.getBytes(StandardCharsets.UTF_8);
+                }
+
+                outputStream.write(outputData);
                 outputStream.flush();
               } catch (HopValueException e) {
                 throw new HopTransformException(
@@ -224,8 +273,6 @@ public class WebServiceServlet extends BaseHttpServlet 
implements IHopServerPlug
       pipeline.startThreads();
       pipeline.waitUntilFinished();
 
-      response.setStatus(HttpServletResponse.SC_OK);
-
     } catch (Exception e) {
       throw new ServletException("Error producing web service output", e);
     }
diff --git a/engine/src/main/java/org/apache/hop/www/service/WebService.java 
b/engine/src/main/java/org/apache/hop/www/service/WebService.java
index 21a85451c2..40101ac6c9 100644
--- a/engine/src/main/java/org/apache/hop/www/service/WebService.java
+++ b/engine/src/main/java/org/apache/hop/www/service/WebService.java
@@ -18,6 +18,8 @@
 
 package org.apache.hop.www.service;
 
+import lombok.Getter;
+import lombok.Setter;
 import org.apache.hop.metadata.api.HopMetadata;
 import org.apache.hop.metadata.api.HopMetadataBase;
 import org.apache.hop.metadata.api.HopMetadataProperty;
@@ -31,6 +33,8 @@ import org.apache.hop.metadata.api.IHopMetadata;
     image = "ui/images/webservice.svg",
     documentationUrl = "/metadata-types/web-service.html",
     hopMetadataPropertyType = HopMetadataPropertyType.SERVER_WEB_SERVICE)
+@Getter
+@Setter
 public class WebService extends HopMetadataBase implements IHopMetadata {
 
   @HopMetadataProperty private boolean enabled;
@@ -38,9 +42,11 @@ public class WebService extends HopMetadataBase implements 
IHopMetadata {
   @HopMetadataProperty private String transformName;
   @HopMetadataProperty private String fieldName;
   @HopMetadataProperty private String contentType;
+  @HopMetadataProperty private String statusCode;
   @HopMetadataProperty private boolean listingStatus;
   @HopMetadataProperty private String bodyContentVariable;
   @HopMetadataProperty private String runConfigurationName;
+  @HopMetadataProperty private String headerContentVariable;
 
   public WebService() {}
 
@@ -51,149 +57,21 @@ public class WebService extends HopMetadataBase implements 
IHopMetadata {
       String transformName,
       String fieldName,
       String contentType,
+      String statusCode,
       boolean listingStatus,
       String bodyContentVariable,
-      String runConfigurationName) {
+      String runConfigurationName,
+      String headerContentVariable) {
     super(name);
     this.enabled = enabled;
     this.filename = filename;
     this.transformName = transformName;
     this.fieldName = fieldName;
     this.contentType = contentType;
+    this.statusCode = statusCode;
     this.listingStatus = listingStatus;
     this.bodyContentVariable = bodyContentVariable;
     this.runConfigurationName = runConfigurationName;
-  }
-
-  /**
-   * Gets enabled
-   *
-   * @return value of enabled
-   */
-  public boolean isEnabled() {
-    return enabled;
-  }
-
-  /**
-   * @param enabled The enabled to set
-   */
-  public void setEnabled(boolean enabled) {
-    this.enabled = enabled;
-  }
-
-  /**
-   * Gets filename
-   *
-   * @return value of filename
-   */
-  public String getFilename() {
-    return filename;
-  }
-
-  /**
-   * @param filename The filename to set
-   */
-  public void setFilename(String filename) {
-    this.filename = filename;
-  }
-
-  /**
-   * Gets transformName
-   *
-   * @return value of transformName
-   */
-  public String getTransformName() {
-    return transformName;
-  }
-
-  /**
-   * @param transformName The transformName to set
-   */
-  public void setTransformName(String transformName) {
-    this.transformName = transformName;
-  }
-
-  /**
-   * Gets fieldName
-   *
-   * @return value of fieldName
-   */
-  public String getFieldName() {
-    return fieldName;
-  }
-
-  /**
-   * @param fieldName The fieldName to set
-   */
-  public void setFieldName(String fieldName) {
-    this.fieldName = fieldName;
-  }
-
-  /**
-   * Gets contentType
-   *
-   * @return value of contentType
-   */
-  public String getContentType() {
-    return contentType;
-  }
-
-  /**
-   * @param contentType The contentType to set
-   */
-  public void setContentType(String contentType) {
-    this.contentType = contentType;
-  }
-
-  /**
-   * Gets listingStatus
-   *
-   * @return value of listingStatus
-   */
-  public boolean isListingStatus() {
-    return listingStatus;
-  }
-
-  /**
-   * @param listingStatus The listingStatus to set
-   */
-  public void setListingStatus(boolean listingStatus) {
-    this.listingStatus = listingStatus;
-  }
-
-  /**
-   * Gets bodyContentVariable
-   *
-   * @return value of bodyContentVariable
-   */
-  public String getBodyContentVariable() {
-    return bodyContentVariable;
-  }
-
-  /**
-   * Sets bodyContentVariable
-   *
-   * @param bodyContentVariable value of bodyContentVariable
-   */
-  public void setBodyContentVariable(String bodyContentVariable) {
-    this.bodyContentVariable = bodyContentVariable;
-  }
-
-  /**
-   * Gets runConfigurationName
-   *
-   * @return value of runConfigurationName
-   */
-  public String getRunConfigurationName() {
-    return runConfigurationName;
-  }
-
-  /**
-   * Sets runConfigurationName
-   *
-   * @param runConfigurationName value of runConfigurationName
-   */
-  public void setRunConfigurationName(String runConfigurationName) {
-    this.runConfigurationName = runConfigurationName;
+    this.headerContentVariable = headerContentVariable;
   }
 }
diff --git 
a/ui/src/main/java/org/apache/hop/ui/www/service/WebServiceEditor.java 
b/ui/src/main/java/org/apache/hop/ui/www/service/WebServiceEditor.java
index 02ee02c856..31eb27e6c5 100644
--- a/ui/src/main/java/org/apache/hop/ui/www/service/WebServiceEditor.java
+++ b/ui/src/main/java/org/apache/hop/ui/www/service/WebServiceEditor.java
@@ -67,9 +67,11 @@ public class WebServiceEditor extends 
MetadataEditor<WebService> {
   private MetaSelectionLine<PipelineRunConfiguration> wRunConfiguration;
   private TextVar wTransform;
   private TextVar wField;
+  private TextVar wStatusCode;
   private ComboVar wContentType;
   private Button wListStatus;
   private TextVar wBodyContentVariable;
+  private TextVar wHeaderContentVariable;
 
   public WebServiceEditor(HopGui hopGui, MetadataManager<WebService> manager, 
WebService metadata) {
     super(hopGui, manager, metadata);
@@ -236,6 +238,25 @@ public class WebServiceEditor extends 
MetadataEditor<WebService> {
     wField.setLayoutData(fdField);
     lastControl = wlField;
 
+    // Status code field
+    //
+    Label wlStatuscode = new Label(parent, SWT.RIGHT);
+    PropsUi.setLook(wlField);
+    wlStatuscode.setText(BaseMessages.getString(PKG, 
"WebServiceEditor.StatusCodeField.Label"));
+    FormData fdlStatusCode = new FormData();
+    fdlStatusCode.left = new FormAttachment(0, 0);
+    fdlStatusCode.right = new FormAttachment(middle, -margin);
+    fdlStatusCode.top = new FormAttachment(lastControl, 2 * margin);
+    wlStatuscode.setLayoutData(fdlStatusCode);
+    wStatusCode = new TextVar(manager.getVariables(), parent, SWT.SINGLE | 
SWT.LEFT | SWT.BORDER);
+    PropsUi.setLook(wStatusCode);
+    FormData fdStatuscode = new FormData();
+    fdStatuscode.left = new FormAttachment(middle, 0);
+    fdStatuscode.right = new FormAttachment(100, 0);
+    fdStatuscode.top = new FormAttachment(wlStatuscode, 0, SWT.CENTER);
+    wStatusCode.setLayoutData(fdStatuscode);
+    lastControl = wlStatuscode;
+
     // Content type
     //
     Label wlContentType = new Label(parent, SWT.RIGHT);
@@ -303,6 +324,31 @@ public class WebServiceEditor extends 
MetadataEditor<WebService> {
     wBodyContentVariable.setLayoutData(fdBodyContentVariable);
     lastControl = wlBodyContentVariable;
 
+    // HeaderContentVariable to read from
+    //
+    Label wlHeaderContentVariable = new Label(parent, SWT.RIGHT);
+    PropsUi.setLook(wlHeaderContentVariable);
+    wlHeaderContentVariable.setText(
+        BaseMessages.getString(PKG, 
"WebServiceEditor.HeaderContentVariable.Label"));
+    wlHeaderContentVariable.setToolTipText(
+        BaseMessages.getString(PKG, 
"WebServiceEditor.HeaderContentVariable.Tooltip"));
+    FormData fdlHeaderContentVariable = new FormData();
+    fdlHeaderContentVariable.left = new FormAttachment(0, 0);
+    fdlHeaderContentVariable.right = new FormAttachment(middle, -margin);
+    fdlHeaderContentVariable.top = new FormAttachment(lastControl, 2 * margin);
+    wlHeaderContentVariable.setLayoutData(fdlHeaderContentVariable);
+    wHeaderContentVariable =
+        new TextVar(manager.getVariables(), parent, SWT.SINGLE | SWT.LEFT | 
SWT.BORDER);
+    wHeaderContentVariable.setToolTipText(
+        BaseMessages.getString(PKG, 
"WebServiceEditor.HeaderContentVariable.Tooltip"));
+    PropsUi.setLook(wHeaderContentVariable);
+    FormData fdHeaderContentVariable = new FormData();
+    fdHeaderContentVariable.left = new FormAttachment(middle, 0);
+    fdHeaderContentVariable.right = new FormAttachment(100, 0);
+    fdHeaderContentVariable.top = new FormAttachment(wlHeaderContentVariable, 
0, SWT.CENTER);
+    wHeaderContentVariable.setLayoutData(fdHeaderContentVariable);
+    lastControl = wlHeaderContentVariable;
+
     setWidgetsContent();
 
     // Add listener to detect change after loading data
@@ -312,9 +358,11 @@ public class WebServiceEditor extends 
MetadataEditor<WebService> {
     wFilename.addListener(SWT.Modify, modifyListener);
     wTransform.addListener(SWT.Modify, modifyListener);
     wField.addListener(SWT.Modify, modifyListener);
+    wStatusCode.addListener(SWT.Modify, modifyListener);
     wContentType.addListener(SWT.Modify, modifyListener);
     wListStatus.addListener(SWT.Selection, modifyListener);
     wBodyContentVariable.addListener(SWT.Modify, modifyListener);
+    wHeaderContentVariable.addListener(SWT.Modify, modifyListener);
     wRunConfiguration.addListener(SWT.Selection, modifyListener);
   }
 
@@ -416,9 +464,11 @@ public class WebServiceEditor extends 
MetadataEditor<WebService> {
     wFilename.setText(Const.NVL(ws.getFilename(), ""));
     wTransform.setText(Const.NVL(ws.getTransformName(), ""));
     wField.setText(Const.NVL(ws.getFieldName(), ""));
+    wStatusCode.setText(Const.NVL(ws.getStatusCode(), ""));
     wContentType.setText(Const.NVL(ws.getContentType(), ""));
     wListStatus.setSelection(ws.isListingStatus());
     wBodyContentVariable.setText(Const.NVL(ws.getBodyContentVariable(), ""));
+    wHeaderContentVariable.setText(Const.NVL(ws.getHeaderContentVariable(), 
""));
     try {
       wRunConfiguration.fillItems();
       wRunConfiguration.setText(Const.NVL(ws.getRunConfigurationName(), ""));
@@ -434,9 +484,11 @@ public class WebServiceEditor extends 
MetadataEditor<WebService> {
     ws.setFilename(wFilename.getText());
     ws.setTransformName(wTransform.getText());
     ws.setFieldName(wField.getText());
+    ws.setStatusCode(wStatusCode.getText());
     ws.setContentType(wContentType.getText());
     ws.setListingStatus(wListStatus.getSelection());
     ws.setBodyContentVariable(wBodyContentVariable.getText());
+    ws.setHeaderContentVariable(wHeaderContentVariable.getText());
     ws.setRunConfigurationName(wRunConfiguration.getText());
   }
 
diff --git 
a/ui/src/main/java/org/apache/hop/ui/www/service/WebServiceGuiPlugin.java 
b/ui/src/main/java/org/apache/hop/ui/www/service/WebServiceGuiPlugin.java
index 1644dc54fc..654b4a0fe3 100644
--- a/ui/src/main/java/org/apache/hop/ui/www/service/WebServiceGuiPlugin.java
+++ b/ui/src/main/java/org/apache/hop/ui/www/service/WebServiceGuiPlugin.java
@@ -104,8 +104,10 @@ public class WebServiceGuiPlugin {
                   transformMeta.getName(),
                   fieldName,
                   "text/plain",
+                  null,
                   false,
                   null,
+                  null,
                   null);
           manager.newMetadata(webService);
           return;
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/www/service/messages/messages_en_US.properties
 
b/ui/src/main/resources/org/apache/hop/ui/www/service/messages/messages_en_US.properties
index a3f249bd7c..7c8cb4612e 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/www/service/messages/messages_en_US.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/www/service/messages/messages_en_US.properties
@@ -18,9 +18,12 @@
 
 WebServiceEditor.BodyContentVariable.Label=Request body content variable
 WebServiceEditor.BodyContentVariable.Tooltip=This is the name of the variable 
which at runtime will contain the content of the request body content. \nThis 
is useful when doing a POST against the webservice.
+WebServiceEditor.HeaderContentVariable.Label=Request header content variable
+WebServiceEditor.HeaderContentVariable.Tooltip=This is the name of the 
variable which at runtime will contain the content of the request header 
content.
 WebServiceEditor.ContentType.Label=Content type
 WebServiceEditor.Enabled.Label=Enabled
 WebServiceEditor.Field.Label=Output field
+WebServiceEditor.StatusCodeField.Label=Status Code field
 WebServiceEditor.Filename.Label=Filename on the server
 WebServiceEditor.ListStatus.Label=List status on server
 WebServiceEditor.Name.Label=Name

Reply via email to