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()