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

github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/main by this push:
     new c67bfad762 fix(workflow-operator): validate the spam operator's result 
attributes like its siblings (#8483)
c67bfad762 is described below

commit c67bfad762b402a2b8ebf5a0cab9b625bffc9784
Author: Prateek Ganigi <[email protected]>
AuthorDate: Thu Sep 17 03:58:38 2026 +0000

    fix(workflow-operator): validate the spam operator's result attributes like 
its siblings (#8483)
    
    ### What changes were proposed in this PR?
    
    `HuggingFaceSpamSMSDetectionOpDesc.getOutputSchemas` was the only one of
    the four legacy Hugging Face operators that neither validated its
    result-attribute names nor keyed the input schema by port id.
    
    `getOutputSchemas` is called as the user configures an operator, so it
    needs an answer for "not filled in yet".
    `HuggingFaceSentimentAnalysisOpDesc` and
    `HuggingFaceIrisLogisticRegressionOpDesc` answer by returning `null`;
    this operator instead passed the unset name straight into `Schema.add`.
    It now returns `null` when either `resultAttributeSpam` or
    `resultAttributeProbability` is null or blank, matching its siblings.
    
    The input schema is also now read as
    `inputSchemas(operatorInfo.inputPorts.head.id)` rather than
    `inputSchemas.values.head`, consistent with the sibling operators. With
    a single input port these are equivalent, so this is a consistency
    change rather than a behavioral fix.
    
    Unifying the error contract across all four legacy operators, two return
    `null`, one throws, this one did neither — is a broader question and is
    not attempted here.
    
    ### Any related issues?
    
    Closes #8482
    
    ### How was this PR tested?
    
    347 tests pass across the `huggingFace` and operator-metadata suites,
    and `scalafmtCheck` is clean for main and test sources. Two tests were
    added to `HuggingFaceSpamSMSDetectionOpDescSpec` covering an unset and a
    blank name for both result attributes. Reverting the operator change and
    re-running makes exactly those two tests fail, confirming they exercise
    the fix; the existing happy-path test already keys the input schema by
    the declared input port, so it covers the lookup change.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Yes, this PR was co-authored with Claude in compliance with ASF policy.
    
    ---------
    
    Co-authored-by: Xuan Gu <[email protected]>
---
 .../HuggingFaceSpamSMSDetectionOpDesc.scala        |  7 +++++-
 .../HuggingFaceSpamSMSDetectionOpDescSpec.scala    | 29 ++++++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/huggingFace/HuggingFaceSpamSMSDetectionOpDesc.scala
 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/huggingFace/HuggingFaceSpamSMSDetectionOpDesc.scala
index 0daa7cd4aa..5ac3c03858 100644
--- 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/huggingFace/HuggingFaceSpamSMSDetectionOpDesc.scala
+++ 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/huggingFace/HuggingFaceSpamSMSDetectionOpDesc.scala
@@ -87,8 +87,13 @@ class HuggingFaceSpamSMSDetectionOpDesc extends 
PythonOperatorDescriptor {
   override def getOutputSchemas(
       inputSchemas: Map[PortIdentity, Schema]
   ): Map[PortIdentity, Schema] = {
+    if (
+      resultAttributeSpam == null || resultAttributeSpam.trim.isEmpty ||
+      resultAttributeProbability == null || 
resultAttributeProbability.trim.isEmpty
+    )
+      return null
     Map(
-      operatorInfo.outputPorts.head.id -> inputSchemas.values.head
+      operatorInfo.outputPorts.head.id -> 
inputSchemas(operatorInfo.inputPorts.head.id)
         .add(resultAttributeSpam, AttributeType.BOOLEAN)
         .add(resultAttributeProbability, AttributeType.DOUBLE)
     )
diff --git 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/huggingFace/HuggingFaceSpamSMSDetectionOpDescSpec.scala
 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/huggingFace/HuggingFaceSpamSMSDetectionOpDescSpec.scala
index f7d39d7beb..e028a986ac 100644
--- 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/huggingFace/HuggingFaceSpamSMSDetectionOpDescSpec.scala
+++ 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/huggingFace/HuggingFaceSpamSMSDetectionOpDescSpec.scala
@@ -82,6 +82,35 @@ class HuggingFaceSpamSMSDetectionOpDescSpec extends 
AnyFlatSpec with Matchers {
     schema.getAttribute("score").getType shouldBe AttributeType.DOUBLE
   }
 
+  it should "return null while a result attribute is still unset" in {
+    // getOutputSchemas is called continuously as the user configures the 
operator,
+    // so an unset name means "no schema yet", not "build one with a null 
column".
+    // The sibling operators (sentiment analysis, iris) already answer this 
way.
+    val in = Schema().add("msg", AttributeType.STRING)
+    val ports = Map((new 
HuggingFaceSpamSMSDetectionOpDesc).operatorInfo.inputPorts.head.id -> in)
+
+    val noSpamCol = configured()
+    noSpamCol.resultAttributeSpam = null
+    noSpamCol.getOutputSchemas(ports) shouldBe null
+
+    val noScoreCol = configured()
+    noScoreCol.resultAttributeProbability = null
+    noScoreCol.getOutputSchemas(ports) shouldBe null
+  }
+
+  it should "return null when a result attribute is blank" in {
+    val in = Schema().add("msg", AttributeType.STRING)
+    val ports = Map((new 
HuggingFaceSpamSMSDetectionOpDesc).operatorInfo.inputPorts.head.id -> in)
+
+    val blankSpamCol = configured()
+    blankSpamCol.resultAttributeSpam = "   "
+    blankSpamCol.getOutputSchemas(ports) shouldBe null
+
+    val blankScoreCol = configured()
+    blankScoreCol.resultAttributeProbability = ""
+    blankScoreCol.getOutputSchemas(ports) shouldBe null
+  }
+
   "HuggingFaceSpamSMSDetectionOpDesc.generatePythonCode" should
     "emit the spam-detection pipeline carrying the configured columns 
(encoded)" in {
     val d = configured()

Reply via email to