-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Ballerina Constraint Package #2850
Comments
Ordered type is defined here: https://ballerina.io/spec/lang/2022R1/#ordering. It cannot be described by a Ballerina type definition. OrderedType is an approximation: any Ballerina ordered type will be an OrderedType. |
uniqueItems only makes sense when member type is subtype of anydata, i.e. applicable only to subtype of |
I don't understand what minContains and maxContains mean. Is there a real use case for multipleOf? This syntax is wrong:
Needs a colon not an equals. |
You might be able to handle some cases with a constraint on the annotation record type. An annotation is a qualified name. So you can have a constrain module that provides different annotations for different basic types e.g. But in general the constraint checker code will need to do some checking. It won't all be done declaratively by the annotation mechanism. |
This is a terrible idea. They are totally different things.
|
This is wrong |
Updated the proposal for the applicable types with the correct terminology. |
These 2 constraints were added with the idea of validating the minimum and the maximum value that an any ordered type array or map can have.
// Cash withdrawal from an ATM
type CashWithdrawal record {|
string currency;
@constrain {
multipleOf: 100
}
int amount;
|};
Updated the proposal. |
Updated the proposal. |
Agree. It acts as a metadata / hint of the content. So, we will remove this |
This is much better than the proposed method IMO. As explained, we can have different annotations for different basic types where each of these will define a separate associated record type. public annotation StringConstraints String on type, record field;
public annotation IntConstraints Int on type, record field;
... type StringConstraints record {|
int length?;
int minLength?;
int maxLength?;
string pattern?;
// ... all the finalized constraints for string type should go here
|};
type IntConstraints record {|
int minValue?;
int maxValue?;
int minValueExclusive?;
int maxValueExclusive?;
// ... all the finalized constraints for int type should go here
|}; @jclark Since package name is If so, the example would be as follows: import ballerina/constraint;
import ballerina/log;
type Person record {|
string name;
@constraint:Int {
minValue: 18
}
int age;
@constraint:String {
pattern: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$"
}
string email;
|};
public function main() {
Person person = {name: "Chanaka", age: 16, email: "[email protected]"};
error? validation = constraint:validate(person);
if validation is error {
log:printError("Failed to validate person details", validation);
}
// business logic
} |
@sameerajayasoma do let us know if you have any feedback. |
We had a meeting today with @jclark to discuss about the proposal and decided followings:
Updated the proposal according to these. |
I thought we were going to have e.g. constraint:Int and constraint:String. Numbers require some subtlety to deal with the case where a value might allow a union:
multipleOf makes sense for only int and decimal. |
Yes. That is true. I meant to express the same but sorry for not explaining it properly. Thanks for the clarification. Updated my last comment to be less ambiguous. |
@jclark The proposed API works successfully with the Case 1: import ballerina/constraint;
import ballerina/log;
type Foo record {
@constraint:String {
length: 6
}
string value;
};
public function main() {
Foo foo = {value: "s3cr3t"};
error? validation = constraint:validate(foo);
if validation is error {
log:printError("Failed to validate details", validation);
}
// business logic
} Case 2: import ballerina/constraint;
import ballerina/log;
@constraint:Int { minValue: 0 }
type PositiveInt int;
public function main() {
PositiveInt age = 18;
error? validation = constraint:validate(age);
if validation is error {
log:printError("Failed to validate age", validation);
}
// business logic
} In case-2, there is no way to get the annotations attached to the |
I agree the API isn't right. Does it really make sense to get the constraints from the value being validated? I don't think so. I think you should pass in the typedesc to be used for validation as a parameter, something like:
e.g.
|
Thanks @jclark. This API is better and solve the problem we have. Also, it works successfully with the both cases mentioned above. |
I would not return the validated value cg
|
I understand why we had to design the validate method to return the value it validates. But I would use the following signature for the validation method. I know that we don't have the syntax to specify some typedesc values.
e.g.,
|
My experience of writing Ballerina code is that it works better to have a function Your example is not realistic: you almost always want to do something with the value you have validated (otherwise why would you validate it?). Apart from that, this approach nicely allows the typedesc value to be defaulted. |
Regarding the regexp match for string types, we would go with the name Sample : string:RegExp regExp = re `([0-9]{10})|(\+[0-9]{11})`;
@constraint:String {pattern: regExp}
type PhoneNumber string;
type User record {|
string name;
@constraint:String {pattern: re `male|female`}
string gender?;
int age;
@constraint:String {pattern: re `([0-9]{9}[v|V]|[0-9]{12})`}
string nic;
|};
type UserAdvanced record {|
*User;
PhoneNumber contactNumber;
@constraint:String {pattern: re `([a-zA-Z0-9._%\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,6})*`}
string email;
|}; |
Summary
Ballerina Constraint package will provide features to validate the values that have been assigned to Ballerina types. This proposal is to introduce the new package that supports for the validation.
Goals
Introduce a new standard library package which has APIs to validate the values that have been assigned to Ballerina types.
Motivation
Right now, the values assigned to Ballerina types cannot be validated further. As an example, according to the definition of int type in Ballerina specification:
It cannot be further constrained as the user wishes. As an example, the
age
of thePerson
cannot be validated for a positive integer. Likewise, there is no way to constraint the values assigned to Ballerina types as of now. With this proposed package, that can be done with the use of an annotation which is binded to the type.Also, this support is available in the other language specification such as XML Schema Part 2, JSON schema validation, OpenAPI specification and JSR 303.
Description
The XML Schema Part 2, JSON schema validation, OpenAPI specification and JSR 303 considered as references for designing this package. The highlighted validation rules/keywords are used for the proposed design for Ballerina.
Constraints of XML Schema
Example:
References:
Constraints of OpenAPI Specification
Example:
References:
Constraints of JSON Schema
Example:
References:
Constraints of Java
NOTE: A whitespace have been added between
@
symbol and the constraint name, in order to remove tagging GitHub users and organizations.@ Null
@ NotNull
@ AssertTrue
@ AssertFalse
@ Min
@ Max
@ DecimalMin
@ DecimalMax
@ Negative
@ NegativeOrZero
@ Positive
@ PositiveOrZero
@ Size
@ Digits
@ Past
@ PastOrPresent
@ Future
@ FutureOrPresent
@ Pattern
@ NotEmpty
@ NotBlank
@ Email
References:
Proposed Constraints for Ballerina
The following constraints are proposed for Ballerina.
any ordered type T
T
int
,decimal
int
,decimal
string
,xml
,table
,list
,map
int
anydata[]
,map<anydata>
boolean
string
xml
SchemaValid
record (defined below)decimal
int
mapping
string[][]
mapping
map<string[]>
SchemaValid
record definition used forschemaValid
constrain of above table:Proposed APIs
The
ballerina/constraint
package provides different annotations for different basic types e.g.@constraint:String
for strings,@constraint:Map
for maps etc. each of these will define a separate associated record type. These annotations are attached to thetype
orrecord field
attachment points.Annotation
Associated Record Types
Annotation Mappings
int
@constraint:Int
float
@constraint:float
int
|float
|decimal
@constraint:Number
string
@constraint:String
any[]
@constraint:Array
Function
The package has the public function that the developer is expected to call with the value that needs to be validated along with its type descriptor. Returns
typedesc<anydata>
if the validation is successful, or else anerror
if the validation is unsuccessful or if there is an issue with the constraint value.NOTE: In general the constraint checker code will need to do some checking on the annotation with the attached basic data type. It won't all be done declaratively by the annotation mechanism.
Examples
Related issue: #2788
The text was updated successfully, but these errors were encountered: