From 5c20bb222ac5cddce9f1f8d01cb5d5bbe96f7664 Mon Sep 17 00:00:00 2001
From: prakanth <50439067+prakanth97@users.noreply.github.com>
Date: Wed, 29 May 2024 11:06:53 +0530
Subject: [PATCH 1/6] Add constraint annotation validation
---
ballerina/Ballerina.toml | 12 ++++++--
ballerina/CompilerPlugin.toml | 2 +-
ballerina/Dependencies.toml | 4 +--
ballerina/build.gradle | 15 ++++++++++
ballerina/tests/fromXml_test.bal | 2 +-
ballerina/xml_api.bal | 2 ++
build-config/resources/Ballerina.toml | 6 ++++
build.gradle | 1 +
gradle.properties | 2 ++
native/build.gradle | 2 ++
.../lib/data/xmldata/io/DataReaderTask.java | 8 +++++
.../lib/data/xmldata/utils/Constants.java | 26 +++++++++-------
.../lib/data/xmldata/utils/DataUtils.java | 22 ++++++++++++++
.../lib/data/xmldata/utils/DiagnosticLog.java | 4 +--
.../lib/data/xmldata/xml/Native.java | 30 +++++++++++++++----
.../lib/data/xmldata/xml/XmlParser.java | 6 ++--
native/src/main/java/module-info.java | 3 +-
17 files changed, 118 insertions(+), 29 deletions(-)
diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml
index e3597f6..be28c5d 100644
--- a/ballerina/Ballerina.toml
+++ b/ballerina/Ballerina.toml
@@ -1,7 +1,7 @@
[package]
org = "ballerina"
name = "data.xmldata"
-version = "0.1.0"
+version = "0.1.1"
authors = ["Ballerina"]
keywords = ["xml"]
repository = "https://github.com/ballerina-platform/module-ballerina-data-xmldata"
@@ -12,5 +12,11 @@ export = ["data.xmldata"]
[[platform.java17.dependency]]
groupId = "io.ballerina.lib"
artifactId = "data-native"
-version = "0.1.0"
-path = "../native/build/libs/data.xmldata-native-0.1.0.jar"
+version = "0.1.1"
+path = "../native/build/libs/data.xmldata-native-0.1.1-SNAPSHOT.jar"
+
+[[platform.java17.dependency]]
+groupId = "io.ballerina.stdlib"
+artifactId = "constraint-native"
+version = "1.5.0"
+path = "./lib/constraint-native-1.5.0.jar"
diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml
index 7eef6a8..98592f5 100644
--- a/ballerina/CompilerPlugin.toml
+++ b/ballerina/CompilerPlugin.toml
@@ -3,4 +3,4 @@ id = "constraint-compiler-plugin"
class = "io.ballerina.lib.data.xmldata.compiler.XmldataCompilerPlugin"
[[dependency]]
-path = "../compiler-plugin/build/libs/data.xmldata-compiler-plugin-0.1.0.jar"
+path = "../compiler-plugin/build/libs/data.xmldata-compiler-plugin-0.1.1-SNAPSHOT.jar"
diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml
index 544d7c8..438831b 100644
--- a/ballerina/Dependencies.toml
+++ b/ballerina/Dependencies.toml
@@ -5,12 +5,12 @@
[ballerina]
dependencies-toml-version = "2"
-distribution-version = "2201.9.0"
+distribution-version = "2201.9.0-20240502-141200-0a49fa42"
[[package]]
org = "ballerina"
name = "data.xmldata"
-version = "0.1.0"
+version = "0.1.1"
dependencies = [
{org = "ballerina", name = "io"},
{org = "ballerina", name = "jballerina.java"},
diff --git a/ballerina/build.gradle b/ballerina/build.gradle
index c63d338..4fc312a 100644
--- a/ballerina/build.gradle
+++ b/ballerina/build.gradle
@@ -66,10 +66,25 @@ ballerina {
testCoverageParam = "--code-coverage --coverage-format=xml --includes=io.ballerina.lib.data.xmldata*:ballerina.*"
}
+configurations {
+ externalJars
+}
+
+dependencies {
+ externalJars(group: 'io.ballerina.stdlib', name: 'constraint-native', version: "${stdlibConstraintVersion}") {
+ transitive = false
+ }
+}
+
task updateTomlFiles {
doLast {
+ def stdlibDependentConstraintNativeVersion = stdlibConstraintVersion
+ def stdlibDependentConstraintVersion = stripBallerinaExtensionVersion("${stdlibDependentConstraintNativeVersion}")
+
def newConfig = ballerinaTomlFilePlaceHolder.text.replace("@project.version@", project.version)
newConfig = newConfig.replace("@toml.version@", tomlVersion)
+ newConfig = newConfig.replace("@stdlib.constraintnative.version@", stdlibDependentConstraintNativeVersion)
+ newConfig = newConfig.replace("@constraint.version@", stdlibDependentConstraintVersion)
ballerinaTomlFile.text = newConfig
def newCompilerPluginToml = compilerPluginTomlFilePlaceHolder.text.replace("@project.version@", project.version)
diff --git a/ballerina/tests/fromXml_test.bal b/ballerina/tests/fromXml_test.bal
index ea09143..81cbd97 100644
--- a/ballerina/tests/fromXml_test.bal
+++ b/ballerina/tests/fromXml_test.bal
@@ -3081,7 +3081,7 @@ function testXmlToRecordNegative12() {
}
@test:Config {
- groups: ["fromXmlString"]
+ groups: ["fromXmlString", "testFail"]
}
function testCommentMiddleInContentNegative1() {
string xmlStr = string `12`;
diff --git a/ballerina/xml_api.bal b/ballerina/xml_api.bal
index af0acdc..f59e587 100644
--- a/ballerina/xml_api.bal
+++ b/ballerina/xml_api.bal
@@ -68,9 +68,11 @@ public type Options record {|
# Represent the options that can be used to modify the behaviour of projection.
#
# + allowDataProjection - enable or disable projection
+# + enableConstraintValidation - enable or disable constraint validation
public type SourceOptions record {|
*Options;
boolean allowDataProjection = true;
+ boolean enableConstraintValidation = true;
|};
# Represents the error type of the ballerina/data.xmldata module. This error type represents any error that can occur
diff --git a/build-config/resources/Ballerina.toml b/build-config/resources/Ballerina.toml
index 741191d..6459087 100644
--- a/build-config/resources/Ballerina.toml
+++ b/build-config/resources/Ballerina.toml
@@ -14,3 +14,9 @@ groupId = "io.ballerina.lib"
artifactId = "data-native"
version = "@toml.version@"
path = "../native/build/libs/data.xmldata-native-@project.version@.jar"
+
+[[platform.java17.dependency]]
+groupId = "io.ballerina.stdlib"
+artifactId = "constraint-native"
+version = "@constraint.version@"
+path = "./lib/constraint-native-@stdlib.constraintnative.version@.jar"
diff --git a/build.gradle b/build.gradle
index a3939ce..5ff8f93 100644
--- a/build.gradle
+++ b/build.gradle
@@ -68,6 +68,7 @@ subprojects {
dependencies {
/* Standard libraries */
ballerinaStdLibs "io.ballerina.stdlib:io-ballerina:${stdlibIoVersion}"
+ ballerinaStdLibs "io.ballerina.stdlib:constraint-ballerina:${stdlibConstraintVersion}"
}
}
diff --git a/gradle.properties b/gradle.properties
index 68e8d52..9c48091 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -12,4 +12,6 @@ githubJohnrengelmanShadowVersion=8.1.1
underCouchDownloadVersion=4.0.4
researchgateReleaseVersion=2.8.0
ballerinaGradlePluginVersion=2.0.1
+
stdlibIoVersion=1.6.0
+stdlibConstraintVersion=1.5.0
diff --git a/native/build.gradle b/native/build.gradle
index 250c984..ceb83f2 100644
--- a/native/build.gradle
+++ b/native/build.gradle
@@ -33,6 +33,8 @@ dependencies {
implementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}"
implementation group: 'org.ballerinalang', name: 'ballerina-runtime', version: "${ballerinaLangVersion}"
implementation group: 'org.ballerinalang', name: 'value', version: "${ballerinaLangVersion}"
+
+ implementation group: 'io.ballerina.stdlib', name: 'constraint-native', version: "${stdlibConstraintVersion}"
}
checkstyle {
diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/io/DataReaderTask.java b/native/src/main/java/io/ballerina/lib/data/xmldata/io/DataReaderTask.java
index 4ec6d0f..4acd225 100644
--- a/native/src/main/java/io/ballerina/lib/data/xmldata/io/DataReaderTask.java
+++ b/native/src/main/java/io/ballerina/lib/data/xmldata/io/DataReaderTask.java
@@ -17,6 +17,7 @@
*/
package io.ballerina.lib.data.xmldata.io;
+import io.ballerina.lib.data.xmldata.utils.DataUtils;
import io.ballerina.lib.data.xmldata.utils.DiagnosticErrorCode;
import io.ballerina.lib.data.xmldata.utils.DiagnosticLog;
import io.ballerina.lib.data.xmldata.xml.XmlParser;
@@ -25,6 +26,7 @@
import io.ballerina.runtime.api.types.MethodType;
import io.ballerina.runtime.api.types.ObjectType;
import io.ballerina.runtime.api.utils.TypeUtils;
+import io.ballerina.runtime.api.values.BError;
import io.ballerina.runtime.api.values.BMap;
import io.ballerina.runtime.api.values.BObject;
import io.ballerina.runtime.api.values.BString;
@@ -33,6 +35,8 @@
import java.io.InputStreamReader;
import java.util.function.Consumer;
+import static io.ballerina.lib.data.xmldata.utils.Constants.ENABLE_CONSTRAINT_VALIDATION;
+
/**
* This class will read data from a Ballerina Stream of byte blocks, in non-blocking manner.
*
@@ -88,6 +92,10 @@ public void run() {
try (var byteBlockSteam = new BallerinaByteBlockInputStream(env, iteratorObj, resolveNextMethod(iteratorObj),
resolveCloseMethod(iteratorObj), resultConsumer)) {
Object result = XmlParser.parse(new InputStreamReader(byteBlockSteam), options, typed.getDescribingType());
+ if (!(result instanceof BError)) {
+ result = DataUtils.validateConstraints(result, typed,
+ (Boolean) options.get(ENABLE_CONSTRAINT_VALIDATION));
+ }
future.complete(result);
} catch (Exception e) {
future.complete(DiagnosticLog.error(DiagnosticErrorCode.STREAM_BROKEN, e.getMessage()));
diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java
index ace7fa3..c7e4691 100644
--- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java
+++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/Constants.java
@@ -36,27 +36,31 @@ public class Constants {
private Constants() {}
- public static final String UNDERSCORE = "_";
- public static final String COLON = ":";
- public static final MapType JSON_MAP_TYPE = TypeCreator.createMapType(PredefinedTypes.TYPE_JSON);
- public static final ArrayType JSON_ARRAY_TYPE = TypeCreator.createArrayType(PredefinedTypes.TYPE_JSON);
+ public static final int NS_PREFIX_BEGIN_INDEX = BXmlItem.XMLNS_NS_URI_PREFIX.length();
+ public static final int DEFAULT_TYPE_FLAG = 2049;
+
public static final String FIELD = "$field$.";
public static final String NAMESPACE = "Namespace";
- public static final BString URI = StringUtils.fromString("uri");
- public static final BString PREFIX = StringUtils.fromString("prefix");
+ public static final String UNDERSCORE = "_";
+ public static final String COLON = ":";
public static final String ATTRIBUTE = "Attribute";
- public static final int DEFAULT_TYPE_FLAG = 2049;
public static final String NAME = "Name";
- public static final BString VALUE = StringUtils.fromString("value");
public static final String CONTENT = "#content";
public static final String FIELD_REGEX = "\\$field\\$\\.";
- public static final int NS_PREFIX_BEGIN_INDEX = BXmlItem.XMLNS_NS_URI_PREFIX.length();
public static final String RECORD = "record";
public static final String RECORD_OR_MAP = "record or map";
public static final String ANON_TYPE = "$anonType$";
- public static final QualifiedName EXIT_REST_POINT = new QualifiedName("", "$exitRestPoint$", "");
+ public static final String NON_NUMERIC_STRING_REGEX = "[^a-zA-Z\\d\s]";
+
+ public static final BString URI = StringUtils.fromString("uri");
+ public static final BString PREFIX = StringUtils.fromString("prefix");
+ public static final BString VALUE = StringUtils.fromString("value");
public static final BString ATTRIBUTE_PREFIX = StringUtils.fromString("attributePrefix");
public static final BString TEXT_FIELD_NAME = StringUtils.fromString("textFieldName");
public static final BString ALLOW_DATA_PROJECTION = StringUtils.fromString("allowDataProjection");
- public static final String NON_NUMERIC_STRING_REGEX = "[^a-zA-Z\\d\s]";
+ public static final BString ENABLE_CONSTRAINT_VALIDATION = StringUtils.fromString("enableConstraintValidation");
+
+ public static final MapType JSON_MAP_TYPE = TypeCreator.createMapType(PredefinedTypes.TYPE_JSON);
+ public static final ArrayType JSON_ARRAY_TYPE = TypeCreator.createArrayType(PredefinedTypes.TYPE_JSON);
+ public static final QualifiedName EXIT_REST_POINT = new QualifiedName("", "$exitRestPoint$", "");
}
diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DataUtils.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DataUtils.java
index 65ac6f7..49f392c 100644
--- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DataUtils.java
+++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DataUtils.java
@@ -38,6 +38,7 @@
import io.ballerina.runtime.api.values.BMap;
import io.ballerina.runtime.api.values.BString;
import io.ballerina.runtime.api.values.BTypedesc;
+import io.ballerina.stdlib.constraint.Constraints;
import java.util.ArrayList;
import java.util.HashMap;
@@ -767,6 +768,27 @@ private static String addAttributeToRecord(BString prefix, BString uri, String k
return prefix.getValue().concat(Constants.COLON).concat(key);
}
+ public static Object validateConstraints(Object convertedValue, BTypedesc typed, boolean requireValidation) {
+ if (!requireValidation) {
+ return convertedValue;
+ }
+
+ Object result = Constraints.validate(convertedValue, typed);
+ if (result instanceof BError bError) {
+ return DiagnosticLog.createXmlError(getPrintableErrorMsg(bError));
+ }
+ return convertedValue;
+ }
+
+ private static String getPrintableErrorMsg(BError err) {
+ String errorMsg = err.getMessage() != null ? err.getMessage() : "";
+ Object details = err.getDetails();
+ if (details != null && !details.toString().equals("{}")) {
+ errorMsg += ", " + details;
+ }
+ return errorMsg;
+ }
+
/**
* Holds data required for the traversing.
*
diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DiagnosticLog.java b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DiagnosticLog.java
index 4cfdd33..9243cdb 100644
--- a/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DiagnosticLog.java
+++ b/native/src/main/java/io/ballerina/lib/data/xmldata/utils/DiagnosticLog.java
@@ -38,7 +38,7 @@ public class DiagnosticLog {
public static BError error(DiagnosticErrorCode code, Object... args) {
String msg = formatMessage(code, args);
- return getXmlError(msg);
+ return createXmlError(msg);
}
private static String formatMessage(DiagnosticErrorCode code, Object[] args) {
@@ -46,7 +46,7 @@ private static String formatMessage(DiagnosticErrorCode code, Object[] args) {
return MessageFormat.format(msgKey, args);
}
- public static BError getXmlError(String message) {
+ public static BError createXmlError(String message) {
return ErrorCreator.createError(ModuleUtils.getModule(), ERROR, StringUtils.fromString(message),
null, null);
}
diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java
index dfcc6de..80a5d40 100644
--- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java
+++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/Native.java
@@ -20,11 +20,13 @@
import io.ballerina.lib.data.xmldata.io.DataReaderTask;
import io.ballerina.lib.data.xmldata.io.DataReaderThreadPool;
+import io.ballerina.lib.data.xmldata.utils.DataUtils;
import io.ballerina.lib.data.xmldata.utils.DiagnosticErrorCode;
import io.ballerina.lib.data.xmldata.utils.DiagnosticLog;
import io.ballerina.runtime.api.Environment;
import io.ballerina.runtime.api.Future;
import io.ballerina.runtime.api.values.BArray;
+import io.ballerina.runtime.api.values.BError;
import io.ballerina.runtime.api.values.BMap;
import io.ballerina.runtime.api.values.BObject;
import io.ballerina.runtime.api.values.BStream;
@@ -36,6 +38,8 @@
import java.io.InputStreamReader;
import java.io.StringReader;
+import static io.ballerina.lib.data.xmldata.utils.Constants.ENABLE_CONSTRAINT_VALIDATION;
+
/**
* Xml conversion.
*
@@ -45,14 +49,25 @@ public class Native {
public static Object parseAsType(BXml xml, BMap options, BTypedesc typed) {
try {
- return XmlTraversal.traverse(xml, options, typed.getDescribingType());
+ Object convertedValue = XmlTraversal.traverse(xml, options, typed.getDescribingType());
+ if (convertedValue instanceof BError) {
+ return convertedValue;
+ }
+ return DataUtils.validateConstraints(convertedValue, typed,
+ (Boolean) options.get(ENABLE_CONSTRAINT_VALIDATION));
} catch (Exception e) {
- return DiagnosticLog.getXmlError(e.getMessage());
+ return DiagnosticLog.createXmlError(e.getMessage());
}
}
public static Object parseString(BString xml, BMap options, BTypedesc typed) {
try {
- return XmlParser.parse(new StringReader(xml.getValue()), options, typed.getDescribingType());
+ Object convertedValue = XmlParser.parse(new StringReader(xml.getValue()), options,
+ typed.getDescribingType());
+ if (convertedValue instanceof BError) {
+ return convertedValue;
+ }
+ return DataUtils.validateConstraints(convertedValue, typed,
+ (Boolean) options.get(ENABLE_CONSTRAINT_VALIDATION));
} catch (Exception e) {
return DiagnosticLog.error(DiagnosticErrorCode.XML_PARSE_ERROR, e.getMessage());
}
@@ -60,8 +75,13 @@ public static Object parseString(BString xml, BMap options, BTy
public static Object parseBytes(BArray xml, BMap options, BTypedesc typed) {
try {
- return XmlParser.parse(new InputStreamReader(new ByteArrayInputStream(xml.getBytes())), options,
- typed.getDescribingType());
+ Object convertedValue = XmlParser.parse(new InputStreamReader(new ByteArrayInputStream(xml.getBytes())),
+ options, typed.getDescribingType());
+ if (convertedValue instanceof BError) {
+ return convertedValue;
+ }
+ return DataUtils.validateConstraints(convertedValue, typed,
+ (Boolean) options.get(ENABLE_CONSTRAINT_VALIDATION));
} catch (Exception e) {
return DiagnosticLog.error(DiagnosticErrorCode.XML_PARSE_ERROR, e.getMessage());
}
diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlParser.java b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlParser.java
index f095afc..cfca3a4 100644
--- a/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlParser.java
+++ b/native/src/main/java/io/ballerina/lib/data/xmldata/xml/XmlParser.java
@@ -110,9 +110,9 @@ private static void updateOptions(BMap options, XmlParserData x
private void handleXMLStreamException(Exception e) {
String reason = e.getCause() == null ? e.getMessage() : e.getCause().getMessage();
if (reason == null) {
- throw DiagnosticLog.getXmlError(PARSE_ERROR);
+ throw DiagnosticLog.createXmlError(PARSE_ERROR);
}
- throw DiagnosticLog.getXmlError(PARSE_ERROR_PREFIX + reason);
+ throw DiagnosticLog.createXmlError(PARSE_ERROR_PREFIX + reason);
}
public Object parse(Type type, XmlParserData xmlParserData) {
@@ -151,7 +151,7 @@ public Object parse(XmlParserData xmlParserData) {
readNext = parseXmlElements(next, xmlParserData);
}
} catch (NumberFormatException e) {
- throw DiagnosticLog.getXmlError(PARSE_ERROR_PREFIX + e);
+ throw DiagnosticLog.createXmlError(PARSE_ERROR_PREFIX + e);
} catch (BError e) {
throw e;
} catch (Exception e) {
diff --git a/native/src/main/java/module-info.java b/native/src/main/java/module-info.java
index 8fd5c1b..4631e40 100644
--- a/native/src/main/java/module-info.java
+++ b/native/src/main/java/module-info.java
@@ -16,9 +16,10 @@
* under the License.
*/
-module io.ballerina.stdlib.data {
+module io.ballerina.lib.data {
requires io.ballerina.runtime;
requires io.ballerina.lang.value;
+ requires io.ballerina.stdlib.constraint;
requires java.xml;
requires junit;
requires org.apache.commons.lang3;
From 1e4f000daf4ea397d485257a77193e75291c559d Mon Sep 17 00:00:00 2001
From: prakanth <50439067+prakanth97@users.noreply.github.com>
Date: Wed, 29 May 2024 17:16:28 +0530
Subject: [PATCH 2/6] Add constraint validation tests
---
.../tests/constraint_annotation_test.bal | 318 ++++++++++++++++++
ballerina/tests/fromXml_test.bal | 2 +-
2 files changed, 319 insertions(+), 1 deletion(-)
create mode 100644 ballerina/tests/constraint_annotation_test.bal
diff --git a/ballerina/tests/constraint_annotation_test.bal b/ballerina/tests/constraint_annotation_test.bal
new file mode 100644
index 0000000..8187f82
--- /dev/null
+++ b/ballerina/tests/constraint_annotation_test.bal
@@ -0,0 +1,318 @@
+// Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
+//
+// WSO2 LLC. licenses this file to you under the Apache License,
+// Version 2.0 (the "License"); you may not use this file except
+// in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License
+
+import ballerina/constraint;
+import ballerina/test;
+import ballerina/time;
+
+public type ValidationPerson record {|
+ @constraint:String {
+ maxLength: 5,
+ minLength: 2
+ }
+ string name;
+ @constraint:Int {
+ minValueExclusive: 5
+ }
+ int age;
+ @constraint:Float {
+ minValue: 150,
+ maxValue: 185.20,
+ maxFractionDigits: 2
+ }
+ float height;
+ @constraint:Date {
+ option: {
+ value: "PAST",
+ message: "Date of birth should be past value"
+ },
+ message: "Invalid date found for date of birth"
+ }
+ time:Date dob;
+|};
+
+@test:Config {
+ groups: ["constraint-validation"]
+}
+function testValidConstraintAnnotationForParseString() returns error? {
+ string xmlStr = string `
+
+ John
+ 6
+ 180.20
+
+ 1990
+ 12
+ 31
+
+
+ `;
+
+ ValidationPerson person = check parseString(xmlStr);
+ test:assertEquals(person.name, "John");
+ test:assertEquals(person.age, 6);
+ test:assertEquals(person.height, 180.20);
+ test:assertEquals(person.dob.year, 1990);
+ test:assertEquals(person.dob.month, 12);
+ test:assertEquals(person.dob.day, 31);
+}
+
+@constraint:Array {
+ length: 2
+}
+public type Weight decimal[];
+
+public type ValidationItem record {|
+ Weight weight;
+|};
+
+@test:Config {
+ groups: ["constraint-validation"],
+ dataProvider: invalidConstraintAnnotation
+}
+function testInvalidConstraintAnnotationForParseString(string sourceData, typedesc expType, string expectedError) {
+ anydata|Error err = parseString(sourceData, {}, expType);
+ test:assertEquals(err is Error, true);
+ test:assertEquals(( err).message(), expectedError);
+}
+
+function invalidConstraintAnnotation() returns [string, typedesc, string][] {
+ return [
+ [
+ string `
+
+ John Doe
+ 6
+ 180.20
+
+ 1990
+ 12
+ 31
+
+
+ `,
+ ValidationPerson,
+ "Validation failed for '$.name:maxLength' constraint(s)."
+ ],
+ [
+ string `
+
+ John
+ 4
+ 180.20
+
+ 1990
+ 12
+ 31
+
+
+ `,
+ ValidationPerson,
+ "Validation failed for '$.age:minValueExclusive' constraint(s)."
+ ],
+ [
+ string `
+
+ John
+ 6
+ 185.21
+
+ 1990
+ 12
+ 31
+
+
+ `,
+ ValidationPerson,
+ "Validation failed for '$.height:maxValue' constraint(s)."
+ ],
+ [
+ string `
+
+ John
+ 6
+ 167.252
+
+ 1990
+ 12
+ 31
+
+
+ `,
+ ValidationPerson,
+ "Validation failed for '$.height:maxFractionDigits' constraint(s)."
+ ],
+ [
+ string `
+
+ John
+ 6
+ 167.25
+
+ 5000
+ 12
+ 31
+
+
+ `,
+ ValidationPerson,
+ "Date of birth should be past value."
+ ],
+ [
+ string `
+ -
+ 1.2
+ 2.3
+ 3.4
+
+ `,
+ ValidationItem,
+ "Validation failed for '$.weight:length' constraint(s)."
+ ]
+ ];
+}
+
+@test:Config {
+ groups: ["constraint-validation"]
+}
+function testValidConstraintAnnotationForParseAsType() returns error? {
+ xml xmlVal = xml `
+
+ John
+ 6
+ 180.20
+
+ 1990
+ 12
+ 31
+
+
+ `;
+
+ ValidationPerson person = check parseAsType(xmlVal);
+ test:assertEquals(person.name, "John");
+ test:assertEquals(person.age, 6);
+ test:assertEquals(person.height, 180.20);
+ test:assertEquals(person.dob.year, 1990);
+ test:assertEquals(person.dob.month, 12);
+ test:assertEquals(person.dob.day, 31);
+}
+
+@test:Config {
+ groups: ["constraint-validation"],
+ dataProvider: invalidConstraintAnnotationForParseAsType
+}
+function testInvalidConstraintAnnotationForParseAsType(xml sourceData, typedesc expType, string expectedError) {
+ anydata|Error err = parseAsType(sourceData, {}, expType);
+ test:assertEquals(err is Error, true);
+ test:assertEquals(( err).message(), expectedError);
+}
+
+function invalidConstraintAnnotationForParseAsType() returns [xml, typedesc, string][] {
+ return [
+ [
+ xml `
+
+ John Doe
+ 6
+ 180.20
+
+ 1990
+ 12
+ 31
+
+
+ `,
+ ValidationPerson,
+ "Validation failed for '$.name:maxLength' constraint(s)."
+ ],
+ [
+ xml `
+
+ John
+ 4
+ 180.20
+
+ 1990
+ 12
+ 31
+
+
+ `,
+ ValidationPerson,
+ "Validation failed for '$.age:minValueExclusive' constraint(s)."
+ ],
+ [
+ xml `
+
+ John
+ 6
+ 185.21
+
+ 1990
+ 12
+ 31
+
+
+ `,
+ ValidationPerson,
+ "Validation failed for '$.height:maxValue' constraint(s)."
+ ],
+ [
+ xml `
+
+ John
+ 6
+ 167.252
+
+ 1990
+ 12
+ 31
+
+
+ `,
+ ValidationPerson,
+ "Validation failed for '$.height:maxFractionDigits' constraint(s)."
+ ],
+ [
+ xml `
+
+ John
+ 6
+ 167.25
+
+ 5000
+ 12
+ 31
+
+
+ `,
+ ValidationPerson,
+ "Date of birth should be past value."
+ ],
+ [
+ xml `
+ -
+ 1.2
+ 2.3
+ 3.4
+
+ `,
+ ValidationItem,
+ "Validation failed for '$.weight:length' constraint(s)."
+ ]
+ ];
+}
diff --git a/ballerina/tests/fromXml_test.bal b/ballerina/tests/fromXml_test.bal
index 81cbd97..ea09143 100644
--- a/ballerina/tests/fromXml_test.bal
+++ b/ballerina/tests/fromXml_test.bal
@@ -3081,7 +3081,7 @@ function testXmlToRecordNegative12() {
}
@test:Config {
- groups: ["fromXmlString", "testFail"]
+ groups: ["fromXmlString"]
}
function testCommentMiddleInContentNegative1() {
string xmlStr = string `12`;
From 837e389214d9c7d8d8e65cb9e9b7aaf5eacb132d Mon Sep 17 00:00:00 2001
From: prakanth <50439067+prakanth97@users.noreply.github.com>
Date: Wed, 29 May 2024 17:17:24 +0530
Subject: [PATCH 3/6] [Automated] Update the native jar versions
---
ballerina/Dependencies.toml | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml
index 438831b..65af871 100644
--- a/ballerina/Dependencies.toml
+++ b/ballerina/Dependencies.toml
@@ -5,16 +5,30 @@
[ballerina]
dependencies-toml-version = "2"
-distribution-version = "2201.9.0-20240502-141200-0a49fa42"
+distribution-version = "2201.9.0"
+
+[[package]]
+org = "ballerina"
+name = "constraint"
+version = "1.5.0"
+scope = "testOnly"
+dependencies = [
+ {org = "ballerina", name = "jballerina.java"}
+]
+modules = [
+ {org = "ballerina", packageName = "constraint", moduleName = "constraint"}
+]
[[package]]
org = "ballerina"
name = "data.xmldata"
version = "0.1.1"
dependencies = [
+ {org = "ballerina", name = "constraint"},
{org = "ballerina", name = "io"},
{org = "ballerina", name = "jballerina.java"},
- {org = "ballerina", name = "test"}
+ {org = "ballerina", name = "test"},
+ {org = "ballerina", name = "time"}
]
modules = [
{org = "ballerina", packageName = "data.xmldata", moduleName = "data.xmldata"}
@@ -99,3 +113,15 @@ modules = [
{org = "ballerina", packageName = "test", moduleName = "test"}
]
+[[package]]
+org = "ballerina"
+name = "time"
+version = "2.4.0"
+scope = "testOnly"
+dependencies = [
+ {org = "ballerina", name = "jballerina.java"}
+]
+modules = [
+ {org = "ballerina", packageName = "time", moduleName = "time"}
+]
+
From 457e065f062cdffcc9b830902df2d8dc0867b2f3 Mon Sep 17 00:00:00 2001
From: prakanth <50439067+prakanth97@users.noreply.github.com>
Date: Wed, 29 May 2024 18:10:25 +0530
Subject: [PATCH 4/6] Add module ballerina time as dependency
---
build.gradle | 1 +
gradle.properties | 1 +
2 files changed, 2 insertions(+)
diff --git a/build.gradle b/build.gradle
index 5ff8f93..a272ee8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -69,6 +69,7 @@ subprojects {
/* Standard libraries */
ballerinaStdLibs "io.ballerina.stdlib:io-ballerina:${stdlibIoVersion}"
ballerinaStdLibs "io.ballerina.stdlib:constraint-ballerina:${stdlibConstraintVersion}"
+ ballerinaStdLibs "io.ballerina.stdlib:time-ballerina:${stdlibTimeVersion}"
}
}
diff --git a/gradle.properties b/gradle.properties
index 9c48091..47a1199 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -14,4 +14,5 @@ researchgateReleaseVersion=2.8.0
ballerinaGradlePluginVersion=2.0.1
stdlibIoVersion=1.6.0
+stdlibTimeVersion=2.4.0
stdlibConstraintVersion=1.5.0
From 2aae4555d071138dc344efdc2c3f9be682b23e6a Mon Sep 17 00:00:00 2001
From: prakanth <50439067+prakanth97@users.noreply.github.com>
Date: Fri, 14 Jun 2024 12:52:49 +0530
Subject: [PATCH 5/6] Address review suggestions
---
.../tests/constraint_annotation_test.bal | 124 ++++++++++++++++++
.../lib/data/xmldata/io/DataReaderTask.java | 10 +-
.../lib/data/xmldata/xml/Native.java | 27 +---
.../lib/data/xmldata/xml/XmlParser.java | 11 ++
.../lib/data/xmldata/xml/XmlTraversal.java | 13 ++
5 files changed, 152 insertions(+), 33 deletions(-)
diff --git a/ballerina/tests/constraint_annotation_test.bal b/ballerina/tests/constraint_annotation_test.bal
index 8187f82..62e34f2 100644
--- a/ballerina/tests/constraint_annotation_test.bal
+++ b/ballerina/tests/constraint_annotation_test.bal
@@ -42,6 +42,19 @@ public type ValidationPerson record {|
message: "Invalid date found for date of birth"
}
time:Date dob;
+ Family family;
+|};
+
+public type Family record {|
+ @constraint:Array {
+ maxLength: 2,
+ minLength: 1
+ }
+ string[] members;
+ @constraint:Int {
+ maxDigits: 4
+ }
+ int id;
|};
@test:Config {
@@ -58,6 +71,11 @@ function testValidConstraintAnnotationForParseString() returns error? {
12
31
+
+ 2221
+ John
+ Doe
+
`;
@@ -68,6 +86,10 @@ function testValidConstraintAnnotationForParseString() returns error? {
test:assertEquals(person.dob.year, 1990);
test:assertEquals(person.dob.month, 12);
test:assertEquals(person.dob.day, 31);
+ test:assertEquals(person.family.id, 2221);
+ test:assertEquals(person.family.members.length(), 2);
+ test:assertEquals(person.family.members[0], "John");
+ test:assertEquals(person.family.members[1], "Doe");
}
@constraint:Array {
@@ -102,6 +124,11 @@ function invalidConstraintAnnotation() returns [string, typedesc, str
12
31
+
+ 2221
+ John
+ Doe
+
`,
ValidationPerson,
@@ -118,6 +145,11 @@ function invalidConstraintAnnotation() returns [string, typedesc, str
12
31
+
+ 2221
+ John
+ Doe
+
`,
ValidationPerson,
@@ -134,6 +166,11 @@ function invalidConstraintAnnotation() returns [string, typedesc, str
12
31
+
+ 2221
+ John
+ Doe
+
`,
ValidationPerson,
@@ -150,6 +187,11 @@ function invalidConstraintAnnotation() returns [string, typedesc, str
12
31
+
+ 2221
+ John
+ Doe
+
`,
ValidationPerson,
@@ -166,11 +208,38 @@ function invalidConstraintAnnotation() returns [string, typedesc, str
12
31
+
+ 2221
+ John
+ Doe
+
`,
ValidationPerson,
"Date of birth should be past value."
],
+ [
+ string `
+
+ John
+ 6
+ 167.25
+
+ 1999
+ 12
+ 31
+
+
+ 22213
+ John
+ Doe
+ Ross
+
+
+ `,
+ ValidationPerson,
+ "Validation failed for '$.family.id:maxDigits','$.family.members:maxLength' constraint(s)."
+ ],
[
string `
-
@@ -199,6 +268,11 @@ function testValidConstraintAnnotationForParseAsType() returns error? {
12
31
+
+ 2221
+ John
+ Doe
+
`;
@@ -209,6 +283,9 @@ function testValidConstraintAnnotationForParseAsType() returns error? {
test:assertEquals(person.dob.year, 1990);
test:assertEquals(person.dob.month, 12);
test:assertEquals(person.dob.day, 31);
+ test:assertEquals(person.family.members.length(), 2);
+ test:assertEquals(person.family.members[0], "John");
+ test:assertEquals(person.family.members[1], "Doe");
}
@test:Config {
@@ -234,6 +311,11 @@ function invalidConstraintAnnotationForParseAsType() returns [xml, typedesc12
31
+
+ 2221
+ John
+ Doe
+
`,
ValidationPerson,
@@ -250,6 +332,11 @@ function invalidConstraintAnnotationForParseAsType() returns [xml, typedesc12
31
+
+ 2221
+ John
+ Doe
+
`,
ValidationPerson,
@@ -266,6 +353,11 @@ function invalidConstraintAnnotationForParseAsType() returns [xml, typedesc12
31
+
+ 2221
+ John
+ Doe
+
`,
ValidationPerson,
@@ -282,6 +374,11 @@ function invalidConstraintAnnotationForParseAsType() returns [xml, typedesc12
31
+
+ 2221
+ John
+ Doe
+
`,
ValidationPerson,
@@ -298,11 +395,38 @@ function invalidConstraintAnnotationForParseAsType() returns [xml, typedesc12
31
+
+ 2221
+ John
+ Doe
+
`,
ValidationPerson,
"Date of birth should be past value."
],
+ [
+ xml `
+
+ John
+ 6
+ 167.25
+
+ 1999
+ 12
+ 31
+
+
+ 22213
+ John
+ Doe
+ Ross
+
+
+ `,
+ ValidationPerson,
+ "Validation failed for '$.family.id:maxDigits','$.family.members:maxLength' constraint(s)."
+ ],
[
xml `
-
diff --git a/native/src/main/java/io/ballerina/lib/data/xmldata/io/DataReaderTask.java b/native/src/main/java/io/ballerina/lib/data/xmldata/io/DataReaderTask.java
index 4acd225..126d8ad 100644
--- a/native/src/main/java/io/ballerina/lib/data/xmldata/io/DataReaderTask.java
+++ b/native/src/main/java/io/ballerina/lib/data/xmldata/io/DataReaderTask.java
@@ -17,7 +17,6 @@
*/
package io.ballerina.lib.data.xmldata.io;
-import io.ballerina.lib.data.xmldata.utils.DataUtils;
import io.ballerina.lib.data.xmldata.utils.DiagnosticErrorCode;
import io.ballerina.lib.data.xmldata.utils.DiagnosticLog;
import io.ballerina.lib.data.xmldata.xml.XmlParser;
@@ -26,7 +25,6 @@
import io.ballerina.runtime.api.types.MethodType;
import io.ballerina.runtime.api.types.ObjectType;
import io.ballerina.runtime.api.utils.TypeUtils;
-import io.ballerina.runtime.api.values.BError;
import io.ballerina.runtime.api.values.BMap;
import io.ballerina.runtime.api.values.BObject;
import io.ballerina.runtime.api.values.BString;
@@ -35,8 +33,6 @@
import java.io.InputStreamReader;
import java.util.function.Consumer;
-import static io.ballerina.lib.data.xmldata.utils.Constants.ENABLE_CONSTRAINT_VALIDATION;
-
/**
* This class will read data from a Ballerina Stream of byte blocks, in non-blocking manner.
*
@@ -91,11 +87,7 @@ public void run() {
DataReaderTask.ResultConsumer