Skip to content

Commit 07c35d0

Browse files
AnzhiZhangXiao-zhen-Liuchenlica
authored
fix: operators non-blank attribute validation (#4006)
<!-- Thanks for sending a pull request (PR)! Here are some tips for you: 1. If this is your first time, please read our contributor guidelines: [Contributing to Texera](https://github.com/apache/texera/blob/main/CONTRIBUTING.md) 2. Ensure you have added or run the appropriate tests for your PR 3. If the PR is work in progress, mark it a draft on GitHub. 4. Please write your PR title to summarize what this PR proposes, we are following Conventional Commits style for PR titles as well. 5. Be sure to keep the PR description updated to reflect all changes. --> ### What changes were proposed in this PR? <!-- Please clarify what changes you are proposing. The purpose of this section is to outline the changes. Here are some tips for you: 1. If you propose a new API, clarify the use case for a new API. 2. If you fix a bug, you can clarify why it is a bug. 3. If it is a refactoring, clarify what has been changed. 3. It would be helpful to include a before-and-after comparison using screenshots or GIFs. 4. Please consider writing useful notes for better and faster reviews. --> This PR includes two changes: - introduces `javax.validation:validation-api:2.0.1.Final` to the build configuration of the `workflow-core` package, as annotations are needed from this dependency. Some other packages already have this, e.g., `workflow-operator` and `amber`. - adds non-blank / non-null attribute validation annotation to basic chart operators. ### Any related issues, documentation, discussions? <!-- Please use this section to link other resources if not mentioned already. 1. If this PR fixes an issue, please include `Fixes #1234`, `Resolves #1234` or `Closes #1234`. If it is only related, simply mention the issue number. 2. If there is design documentation, please add the link. 3. If there is a discussion in the mailing list, please add the link. --> Fixes #3692 Related to: #3555, #3656, #3192, #3914 ### How was this PR tested? <!-- If tests were added, say they were added here. Or simply mention that if the PR is tested with existing test cases. Make sure to include/update test cases that check the changes thoroughly including negative and positive cases if possible. If it was tested in a way different from regular unit tests, please clarify how you tested step by step, ideally copy and paste-able, so that other reviewers can test and check, and descendants can verify in the future. If tests were not added, please describe why they were not added and/or why it was difficult to add. --> Each operator is added to a testing workflow to check if the validation will pass when those attributes are empty. ### Was this PR authored or co-authored using generative AI tooling? <!-- If generative AI tooling has been used in the process of authoring this PR, please include the phrase: 'Generated-by: ' followed by the name of the tool and its version. If no, write 'No'. Please refer to the [ASF Generative Tooling Guidance](https://www.apache.org/legal/generative-tooling.html) for details. --> GitHub Copilot helped with coding those annotations. --------- Co-authored-by: Xiaozhen Liu <[email protected]> Co-authored-by: Chen Li <[email protected]>
1 parent 66e29dc commit 07c35d0

File tree

20 files changed

+94
-9
lines changed

20 files changed

+94
-9
lines changed

common/workflow-core/build.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ libraryDependencies ++= Seq(
9797

9898
val jacksonVersion = "2.15.1"
9999
libraryDependencies ++= Seq(
100+
"javax.validation" % "validation-api" % "2.0.1.Final",
100101
"com.fasterxml.jackson.core" % "jackson-databind" % jacksonVersion, // Jackson Databind
101102
"com.fasterxml.jackson.module" % "jackson-module-kotlin" % jacksonVersion % Test, // Jackson Kotlin Module
102103
"com.fasterxml.jackson.datatype" % "jackson-datatype-jdk8" % jacksonVersion % Test, // Jackson JDK8 Datatypes

common/workflow-core/src/main/scala/org/apache/amber/core/tuple/Attribute.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.fasterxml.jackson.annotation.JsonCreator;
2323
import com.fasterxml.jackson.annotation.JsonProperty;
2424

25+
import javax.validation.constraints.NotBlank;
2526
import javax.validation.constraints.NotNull;
2627
import java.io.Serializable;
2728

@@ -47,7 +48,7 @@ public Attribute(
4748
}
4849

4950
@JsonProperty(value = "attributeName", required = true)
50-
@NotNull(message = "Attribute name is required")
51+
@NotBlank(message = "Attribute name is required")
5152
public String getName() {
5253
return attributeName;
5354
}

common/workflow-operator/src/main/scala/org/apache/amber/operator/timeSeriesPlot/TimeSeriesPlot.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,23 @@ import org.apache.amber.operator.PythonOperatorDescriptor
2727
import org.apache.amber.operator.metadata.annotations.AutofillAttributeName
2828
import org.apache.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo}
2929

30+
import javax.validation.constraints.{NotBlank, NotNull}
31+
3032
class TimeSeriesOpDesc extends PythonOperatorDescriptor {
3133

3234
@JsonProperty(value = "timeColumn", required = true)
3335
@JsonSchemaTitle("Time Column")
3436
@JsonPropertyDescription("The column containing time/date values (e.g., Date, Timestamp).")
3537
@AutofillAttributeName
38+
@NotNull(message = "Time Column cannot be empty")
3639
var timeColumn: String = ""
3740

3841
@JsonProperty(value = "valueColumn", required = true)
3942
@JsonSchemaTitle("Value Column")
4043
@JsonPropertyDescription("The numerical column to plot on the Y-axis (e.g., Sales, Temperature).")
4144
@JsonSchemaInject(json = """{"enum": "autofill"}""")
4245
@AutofillAttributeName
46+
@NotNull(message = "Value Column cannot be empty")
4347
var valueColumn: String = ""
4448

4549
@JsonProperty(value = "categoryColumn", required = false, defaultValue = "No Selection")
@@ -57,6 +61,7 @@ class TimeSeriesOpDesc extends PythonOperatorDescriptor {
5761
@JsonProperty(value = "line", defaultValue = "line", required = true)
5862
@JsonSchemaTitle("Plot Type")
5963
@JsonPropertyDescription("Select the type of time series plot (line, area).")
64+
@NotBlank(message = "Plot Type cannot be empty")
6065
var plotType: String = "line"
6166

6267
@JsonProperty(value = "slider", defaultValue = "false")

common/workflow-operator/src/main/scala/org/apache/amber/operator/visualization/DotPlot/DotPlotOpDesc.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@ import org.apache.amber.operator.PythonOperatorDescriptor
2828
import org.apache.amber.operator.metadata.annotations.AutofillAttributeName
2929
import org.apache.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo}
3030

31+
import javax.validation.constraints.NotNull
32+
3133
class DotPlotOpDesc extends PythonOperatorDescriptor {
3234

3335
@JsonProperty(value = "Count Attribute", required = true)
3436
@JsonSchemaTitle("Count Attribute")
3537
@JsonPropertyDescription("the attribute for the counting of the dot plot")
3638
@AutofillAttributeName
39+
@NotNull(message = "Count Attribute column cannot be empty")
3740
var countAttribute: String = ""
3841

3942
override def getOutputSchemas(

common/workflow-operator/src/main/scala/org/apache/amber/operator/visualization/IcicleChart/IcicleChartOpDesc.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import org.apache.amber.operator.metadata.annotations.AutofillAttributeName
2929
import org.apache.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo}
3030
import org.apache.amber.operator.visualization.hierarchychart.HierarchySection
3131

32+
import javax.validation.constraints.{NotEmpty, NotNull}
33+
3234
// type constraint: value can only be numeric
3335
@JsonSchemaInject(json = """
3436
{
@@ -45,12 +47,14 @@ class IcicleChartOpDesc extends PythonOperatorDescriptor {
4547
@JsonPropertyDescription(
4648
"hierarchy of attributes from a root (higher-level category) to leaves (lower-level category)"
4749
)
50+
@NotEmpty(message = "Hierarchy path list cannot be empty")
4851
var hierarchy: List[HierarchySection] = List()
4952

5053
@JsonProperty(value = "value", required = true)
5154
@JsonSchemaTitle("Value Column")
5255
@JsonPropertyDescription("the value associated with the size of each sector in the chart")
5356
@AutofillAttributeName
57+
@NotNull(message = "Value column cannot be empty")
5458
var value: String = ""
5559

5660
override def getOutputSchemas(

common/workflow-operator/src/main/scala/org/apache/amber/operator/visualization/barChart/BarChartOpDesc.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import org.apache.amber.operator.PythonOperatorDescriptor
2828
import org.apache.amber.operator.metadata.annotations.AutofillAttributeName
2929
import org.apache.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo}
3030

31+
import javax.validation.constraints.NotNull
32+
3133
//type constraint: value can only be numeric
3234
@JsonSchemaInject(json = """
3335
{
@@ -44,12 +46,14 @@ class BarChartOpDesc extends PythonOperatorDescriptor {
4446
@JsonSchemaTitle("Value Column")
4547
@JsonPropertyDescription("The value associated with each category")
4648
@AutofillAttributeName
49+
@NotNull(message = "Value column cannot be empty")
4750
var value: String = ""
4851

4952
@JsonProperty(required = true)
5053
@JsonSchemaTitle("Fields")
5154
@JsonPropertyDescription("Visualize categorical data in a Bar Chart")
5255
@AutofillAttributeName
56+
@NotNull(message = "Fields cannot be empty")
5357
var fields: String = ""
5458

5559
@JsonProperty(defaultValue = "No Selection", required = false)
@@ -88,8 +92,8 @@ class BarChartOpDesc extends PythonOperatorDescriptor {
8892
)
8993

9094
def manipulateTable(): String = {
91-
assert(value.nonEmpty)
92-
assert(fields.nonEmpty)
95+
assert(value.nonEmpty, "Value column cannot be empty")
96+
assert(fields.nonEmpty, "Fields cannot be empty")
9397
s"""
9498
| table = table.dropna(subset = ['$value', '$fields']) #remove missing values
9599
|""".stripMargin

common/workflow-operator/src/main/scala/org/apache/amber/operator/visualization/bubbleChart/BubbleChartOpDesc.scala

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import org.apache.amber.operator.PythonOperatorDescriptor
2828
import org.apache.amber.operator.metadata.annotations.AutofillAttributeName
2929
import org.apache.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo}
3030

31+
import javax.validation.constraints.NotNull
32+
3133
/**
3234
* Visualization Operator to visualize results as a Bubble Chart
3335
* User specifies 2 columns to use for the x, y labels. Size of bubbles determined via
@@ -40,17 +42,23 @@ class BubbleChartOpDesc extends PythonOperatorDescriptor {
4042
@JsonProperty(value = "xValue", required = true)
4143
@JsonSchemaTitle("X-Column")
4244
@JsonPropertyDescription("Data column for the x-axis")
43-
@AutofillAttributeName var xValue: String = ""
45+
@AutofillAttributeName
46+
@NotNull(message = "xValue column cannot be empty")
47+
var xValue: String = ""
4448

4549
@JsonProperty(value = "yValue", required = true)
4650
@JsonSchemaTitle("Y-Column")
4751
@JsonPropertyDescription("Data column for the y-axis")
48-
@AutofillAttributeName var yValue: String = ""
52+
@AutofillAttributeName
53+
@NotNull(message = "yValue column cannot be empty")
54+
var yValue: String = ""
4955

5056
@JsonProperty(value = "zValue", required = true)
5157
@JsonSchemaTitle("Z-Column")
5258
@JsonPropertyDescription("Data column to determine bubble size")
53-
@AutofillAttributeName var zValue: String = ""
59+
@AutofillAttributeName
60+
@NotNull(message = "zValue column cannot be empty")
61+
var zValue: String = ""
5462

5563
@JsonProperty(value = "enableColor", defaultValue = "false")
5664
@JsonSchemaTitle("Enable Color")
@@ -60,7 +68,9 @@ class BubbleChartOpDesc extends PythonOperatorDescriptor {
6068
@JsonProperty(value = "colorCategory", required = true)
6169
@JsonSchemaTitle("Color-Column")
6270
@JsonPropertyDescription("Picks data column to color bubbles with if color is enabled")
63-
@AutofillAttributeName var colorCategory: String = ""
71+
@AutofillAttributeName
72+
@NotNull(message = "colorCategory column cannot be empty")
73+
var colorCategory: String = ""
6474

6575
override def getOutputSchemas(
6676
inputSchemas: Map[PortIdentity, Schema]

common/workflow-operator/src/main/scala/org/apache/amber/operator/visualization/dumbbellPlot/DumbbellDotConfig.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription}
2323
import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle}
2424
import org.apache.amber.operator.metadata.annotations.AutofillAttributeName
2525

26+
import javax.validation.constraints.NotNull
27+
2628
@JsonSchemaInject(json = """
2729
{
2830
"attributeTypeRules": {
@@ -38,6 +40,7 @@ class DumbbellDotConfig {
3840
@JsonSchemaTitle("Dot Column Value")
3941
@JsonPropertyDescription("value for dot axis")
4042
@AutofillAttributeName
43+
@NotNull(message = "Dot Column Value cannot be empty")
4144
var dotValue: String = ""
4245

4346
}

common/workflow-operator/src/main/scala/org/apache/amber/operator/visualization/dumbbellPlot/DumbbellPlotOpDesc.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import org.apache.amber.operator.metadata.annotations.AutofillAttributeName
2929
import org.apache.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo}
3030

3131
import java.util
32+
import javax.validation.constraints.{NotBlank, NotNull}
3233
import scala.jdk.CollectionConverters.CollectionHasAsScala
3334
//type constraint: measurementColumnName can only be a numeric column
3435
@JsonSchemaInject(json = """
@@ -46,28 +47,33 @@ class DumbbellPlotOpDesc extends PythonOperatorDescriptor {
4647
@JsonSchemaTitle("Category Column Name")
4748
@JsonPropertyDescription("the name of the category column")
4849
@AutofillAttributeName
50+
@NotNull(message = "Category Column Name cannot be empty")
4951
var categoryColumnName: String = ""
5052

5153
@JsonProperty(value = "dumbbellStartValue", required = true)
5254
@JsonSchemaTitle("Dumbbell Start Value")
5355
@JsonPropertyDescription("the start point value of each dumbbell")
56+
@NotBlank(message = "Dumbbell Start Value cannot be empty")
5457
var dumbbellStartValue: String = ""
5558

5659
@JsonProperty(value = "dumbbellEndValue", required = true)
5760
@JsonSchemaTitle("Dumbbell End Value")
5861
@JsonPropertyDescription("the end value of each dumbbell")
62+
@NotBlank(message = "Dumbbell End Value cannot be empty")
5963
var dumbbellEndValue: String = ""
6064

6165
@JsonProperty(value = "measurementColumnName", required = true)
6266
@JsonSchemaTitle("Measurement Column Name")
6367
@JsonPropertyDescription("the name of the measurement column")
6468
@AutofillAttributeName
69+
@NotNull(message = "Measurement Column Name cannot be empty")
6570
var measurementColumnName: String = ""
6671

6772
@JsonProperty(value = "comparedColumnName", required = true)
6873
@JsonSchemaTitle("Compared Column Name")
6974
@JsonPropertyDescription("the column name that is being compared")
7075
@AutofillAttributeName
76+
@NotNull(message = "Compared Column Name cannot be empty")
7177
var comparedColumnName: String = ""
7278

7379
@JsonProperty(value = "dots", required = false)

common/workflow-operator/src/main/scala/org/apache/amber/operator/visualization/filledAreaPlot/FilledAreaPlotOpDesc.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,22 @@ import org.apache.amber.operator.PythonOperatorDescriptor
2828
import org.apache.amber.operator.metadata.annotations.AutofillAttributeName
2929
import org.apache.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo}
3030

31+
import javax.validation.constraints.NotNull
32+
3133
class FilledAreaPlotOpDesc extends PythonOperatorDescriptor {
3234

3335
@JsonProperty(required = true)
3436
@JsonSchemaTitle("X-axis Attribute")
3537
@JsonPropertyDescription("The attribute for your x-axis")
3638
@AutofillAttributeName
39+
@NotNull(message = "X-axis Attribute cannot be empty")
3740
var x: String = ""
3841

3942
@JsonProperty(required = true)
4043
@JsonSchemaTitle("Y-axis Attribute")
4144
@JsonPropertyDescription("The attribute for your y-axis")
4245
@AutofillAttributeName
46+
@NotNull(message = "Y-axis Attribute cannot be empty")
4347
var y: String = ""
4448

4549
@JsonProperty(required = false)

0 commit comments

Comments
 (0)