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

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

commit 1c411dedaad2bb8bd8fd4f5301843ae1f2f1c781
Author: Kary Zheng <[email protected]>
AuthorDate: Wed Jul 22 21:34:19 2026 -0700

    fix(QuiverPlot, RangeSlider): retarget numeric attributeTypeRules to real 
fields (#6810)
    
    ### What changes were proposed in this PR?
    
    Both `QuiverPlotOpDesc` and `RangeSliderOpDesc` carry a class-level
    `@JsonSchemaInject` `attributeTypeRules` block that constrains a field
    named **`value`** to numeric types — but neither operator declares a
    `value` field, so the rule matches nothing and **no type constraint is
    applied**. The frontend column pickers therefore allow columns of any
    type for fields the operator can only handle as numeric, which then
    fails at chart time instead of being prevented up front.
    
    **QuiverPlot** — fields are `x`, `y`, `u`, `v` (all required, no
    `value`). All four are used as numeric vector coordinates in
    `ff.create_quiver(x, y, u, v)`, and the generated code even
    runtime-checks them with `isinstance(value, (int, float))`. So all four
    must be numeric.
    
    **RangeSlider** — fields are `Y-axis` and `X-axis` (no `value`). The
    y-axis column is aggregated (`groupby(X-axis)[Y-axis].mean()/.sum()`),
    so it must be numeric; the x-axis is only a grouping key and may be any
    type.
    
    **Fix:** retarget each rule to the real `@JsonProperty` field name(s):
    
    ```diff
     # QuiverPlot
    -    "value": {
    -      "enum": ["integer", "long", "double"]
    -    }
    +    "x": { "enum": ["integer", "long", "double"] },
    +    "y": { "enum": ["integer", "long", "double"] },
    +    "u": { "enum": ["integer", "long", "double"] },
    +    "v": { "enum": ["integer", "long", "double"] }
    
     # RangeSlider
    -    "value": {
    +    "Y-axis": {
           "enum": ["integer", "long", "double"]
         }
    ```
    
    ### Any related issues, documentation, discussions?
    
    Closes #6795
    
    ### How was this PR tested?
    
    Added a regression test to each spec that reads the class-level
    `@JsonSchemaInject` `json()` via reflection and asserts the
    `attributeTypeRules` keys are the real fields (`x`/`y`/`u`/`v`;
    `Y-axis`) — not `value` — and that each is constrained to the numeric
    enum. Both fail on `main` (the key set is `{"value"}`) and pass with
    this change.
    
    ```
    sbt "WorkflowOperator/testOnly 
org.apache.texera.amber.operator.visualization.quiverPlot.QuiverPlotOpDescSpec 
org.apache.texera.amber.operator.visualization.rangeSlider.RangeSliderOpDescSpec"
    ...
    Tests: succeeded 12, failed 0, canceled 0, ignored 0, pending 0
    All tests passed.
    ```
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Claude Opus 4.8)
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    ---------
    
    Signed-off-by: Kary Zheng <[email protected]>
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
    Co-authored-by: Copilot Autofix powered by AI 
<[email protected]>
---
 .../visualization/quiverPlot/QuiverPlotOpDesc.scala | 11 ++++++++++-
 .../rangeSlider/RangeSliderOpDesc.scala             |  5 +++--
 .../quiverPlot/QuiverPlotOpDescSpec.scala           | 21 +++++++++++++++++++++
 .../rangeSlider/RangeSliderOpDescSpec.scala         | 20 ++++++++++++++++++++
 4 files changed, 54 insertions(+), 3 deletions(-)

diff --git 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/quiverPlot/QuiverPlotOpDesc.scala
 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/quiverPlot/QuiverPlotOpDesc.scala
index 4903c00dd8..991ba362a1 100644
--- 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/quiverPlot/QuiverPlotOpDesc.scala
+++ 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/quiverPlot/QuiverPlotOpDesc.scala
@@ -35,7 +35,16 @@ import javax.validation.constraints.NotNull
 @JsonSchemaInject(json = """
 {
   "attributeTypeRules": {
-    "value": {
+    "x": {
+      "enum": ["integer", "long", "double"]
+    },
+    "y": {
+      "enum": ["integer", "long", "double"]
+    },
+    "u": {
+      "enum": ["integer", "long", "double"]
+    },
+    "v": {
       "enum": ["integer", "long", "double"]
     }
   }
diff --git 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/rangeSlider/RangeSliderOpDesc.scala
 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/rangeSlider/RangeSliderOpDesc.scala
index 051c0ba2cc..6b1425708d 100644
--- 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/rangeSlider/RangeSliderOpDesc.scala
+++ 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/rangeSlider/RangeSliderOpDesc.scala
@@ -32,11 +32,12 @@ import 
org.apache.texera.amber.pybuilder.PythonTemplateBuilder
 
 import javax.validation.constraints.NotNull
 
-// type constraint: value can only be numeric
+// type constraint: Y-axis is aggregated (mean/sum), so it must be numeric;
+// X-axis is only a grouping key and may be any type.
 @JsonSchemaInject(json = """
 {
   "attributeTypeRules": {
-    "value": {
+    "Y-axis": {
       "enum": ["integer", "long", "double"]
     }
   }
diff --git 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/quiverPlot/QuiverPlotOpDescSpec.scala
 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/quiverPlot/QuiverPlotOpDescSpec.scala
index 4331139f78..ee11e3a54e 100644
--- 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/quiverPlot/QuiverPlotOpDescSpec.scala
+++ 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/quiverPlot/QuiverPlotOpDescSpec.scala
@@ -23,11 +23,13 @@ import org.apache.texera.amber.core.tuple.{AttributeType, 
Schema}
 import org.apache.texera.amber.operator.LogicalOp
 import org.apache.texera.amber.operator.metadata.OperatorGroupConstants
 import org.apache.texera.amber.util.JSONUtils.objectMapper
+import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaInject
 import org.scalatest.flatspec.AnyFlatSpec
 import org.scalatest.matchers.should.Matchers
 
 import java.nio.charset.StandardCharsets
 import java.util.Base64
+import scala.jdk.CollectionConverters._
 
 class QuiverPlotOpDescSpec extends AnyFlatSpec with Matchers {
 
@@ -96,4 +98,23 @@ class QuiverPlotOpDescSpec extends AnyFlatSpec with Matchers 
{
     q.u shouldBe "vu"
     q.v shouldBe "vv"
   }
+
+  "QuiverPlotOpDesc @JsonSchemaInject" should
+    "constrain the real coordinate fields (x/y/u/v) to numeric" in {
+    // The rule keys must be actual @JsonProperty names; a key of "value" (no 
such
+    // field) matches nothing, so no numeric constraint reaches the column 
pickers.
+    val ann = 
classOf[QuiverPlotOpDesc].getAnnotation(classOf[JsonSchemaInject])
+    ann should not be null
+    val rules = objectMapper.readTree(ann.json).path("attributeTypeRules")
+    rules.fieldNames().asScala.toSet shouldBe Set("x", "y", "u", "v")
+    rules.fieldNames().asScala.foreach { f =>
+      rules
+        .path(f)
+        .path("enum")
+        .elements()
+        .asScala
+        .map(_.asText())
+        .toSet shouldBe Set("integer", "long", "double")
+    }
+  }
 }
diff --git 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/rangeSlider/RangeSliderOpDescSpec.scala
 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/rangeSlider/RangeSliderOpDescSpec.scala
index d601572c32..706c39a289 100644
--- 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/rangeSlider/RangeSliderOpDescSpec.scala
+++ 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/rangeSlider/RangeSliderOpDescSpec.scala
@@ -23,9 +23,12 @@ import org.apache.texera.amber.core.tuple.{AttributeType, 
Schema}
 import org.apache.texera.amber.operator.LogicalOp
 import org.apache.texera.amber.operator.metadata.OperatorGroupConstants
 import org.apache.texera.amber.util.JSONUtils.objectMapper
+import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaInject
 import org.scalatest.flatspec.AnyFlatSpec
 import org.scalatest.matchers.should.Matchers
 
+import scala.jdk.CollectionConverters._
+
 class RangeSliderOpDescSpec extends AnyFlatSpec with Matchers {
 
   "RangeSliderOpDesc.operatorInfo" should
@@ -74,4 +77,21 @@ class RangeSliderOpDescSpec extends AnyFlatSpec with 
Matchers {
     r.yAxis shouldBe "sales"
     r.duplicateType shouldBe RangeSliderHandleDuplicateFunction.MEAN
   }
+
+  "RangeSliderOpDesc @JsonSchemaInject" should
+    "constrain the aggregated Y-axis to numeric and leave X-axis 
unconstrained" in {
+    // The rule key must be an actual @JsonProperty name; a key of "value" (no 
such
+    // field) matches nothing, so no numeric constraint reaches the column 
pickers.
+    val ann = 
classOf[RangeSliderOpDesc].getAnnotation(classOf[JsonSchemaInject])
+    ann should not be null
+    val rules = objectMapper.readTree(ann.json).path("attributeTypeRules")
+    rules.fieldNames().asScala.toSet shouldBe Set("Y-axis")
+    rules
+      .path("Y-axis")
+      .path("enum")
+      .elements()
+      .asScala
+      .map(_.asText())
+      .toSet shouldBe Set("integer", "long", "double")
+  }
 }

Reply via email to