Skip to content

Commit

Permalink
Merge pull request #29 from TharmiganK/add-pattern-support
Browse files Browse the repository at this point in the history
Add `@pattern` support for constraint package
  • Loading branch information
TharmiganK authored Jan 17, 2023
2 parents 3938521 + 5352f0c commit c28d998
Show file tree
Hide file tree
Showing 32 changed files with 494 additions and 175 deletions.
2 changes: 2 additions & 0 deletions ballerina/constraint.bal
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@ public type NumberConstraints record {|
# + length - The number of characters of the constrained `string` type
# + minLength - The inclusive lower bound of the number of characters of the constrained `string` type
# + maxLength - The inclusive upper bound of the number of characters of the constrained `string` type
# + pattern - The regular expression to be matched with the constrained `string` type
public type StringConstraints record {|
int length?;
int minLength?;
int maxLength?;
string:RegExp pattern?;
|};

# Represents the constraints associated with `anydata[]` type.
Expand Down
26 changes: 25 additions & 1 deletion ballerina/tests/advanced_constraint_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,30 @@ type Person record {
minValue: 18
}
int age;
@String {
pattern: re `([a-zA-Z0-9._%\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,6})*`
}
string email?;
};

@test:Config {}
isolated function testMultipleConstraintSuccess() {
isolated function testMultipleConstraintSuccess1() {
Person rec = {name: "Steve", age: 18};
Person|error validation = validate(rec);
if validation is error {
test:assertFail("Unexpected error found.");
}
}

@test:Config {}
isolated function testMultipleConstraintSuccess2() {
Person rec = {name: "James", age: 18, email: "[email protected]"};
Person|error validation = validate(rec);
if validation is error {
test:assertFail("Unexpected error found.");
}
}

@test:Config {}
isolated function testMultipleConstraintFailure1() {
Person rec = {name: "John", age: 18};
Expand Down Expand Up @@ -83,6 +96,17 @@ isolated function testMultipleConstraintFailure4() {
}
}

@test:Config {}
isolated function testMultipleConstraintFailure5() {
Person rec = {name: "Joe", age: 16, email: "#invalid?mail&address"};
Person|error validation = validate(rec);
if validation is error {
test:assertEquals(validation.message(), "Validation failed for '$.age:minValue','$.email:pattern','$.name:minLength' constraint(s).");
} else {
test:assertFail("Expected error not found.");
}
}

// Testing multiple types of annotations in a single record field

type Student record {
Expand Down
19 changes: 19 additions & 0 deletions ballerina/tests/example_constraint_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ final json & readonly SAMPLE_PAYLOAD = {
"payment_gateway_names": [

],
"phone": "+94123456789",
"presentment_currency": "LKR",
"processed_at": "2022-06-28T15:28:38+05:30",
"processing_method": "",
Expand Down Expand Up @@ -485,6 +486,20 @@ isolated function testSampleFailure11() returns error? {
}
}

@test:Config {}
function testSampleFailure12() returns error? {
Order rec = check SAMPLE_PAYLOAD.cloneWithType();
rec.phone = "1234abcd90";
rec.email = "invalid_email?address@";
rec.customer.email = "This is a invalid email";
Order|error validation = validate(rec);
if validation is error {
test:assertEquals(validation.message(), "Validation failed for '$.customer.email:pattern','$.email:pattern','$.phone:pattern' constraint(s).");
} else {
test:assertFail("Expected error not found.");
}
}

public type Order record {
int app_id?;
CustomerAddress billing_address?;
Expand Down Expand Up @@ -514,6 +529,7 @@ public type Order record {
string customer_locale?;
DiscountApplication[] discount_applications?;
DiscountCode[] discount_codes?;
@String {pattern: re `([a-zA-Z0-9._%\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,6})*`}
string email?;
boolean estimated_taxes?;
string financial_status?;
Expand All @@ -535,6 +551,7 @@ public type Order record {
string payment_details?;
PaymentTerms payment_terms?;
string[] payment_gateway_names?;
@String {pattern: re `([0-9]{10})|(\+[0-9]{11})`}
string phone?;
@String {length: 3}
string presentment_currency?;
Expand Down Expand Up @@ -629,6 +646,7 @@ public type Customer record {
string currency?;
string created_at?;
string first_name?;
@String {pattern: re `([a-zA-Z0-9._%\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,6})*`}
string email?;
Address default_address?;
int id?;
Expand All @@ -641,6 +659,7 @@ public type Customer record {
string note?;
@Int {minValueExclusive: 0}
int orders_count?;
@String {pattern: re `([0-9]{10})|(\+[0-9]{11})`}
string phone?;
SmsMarketingConsent sms_marketing_consent?;
string state?;
Expand Down
54 changes: 52 additions & 2 deletions ballerina/tests/string_constraint_on_record_field_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,45 @@ isolated function testStringConstraintMaxLengthOnRecordFieldFailure() {
}
}

type StringConstraintPatternOnRecordField record {
@String {
pattern: re `[0-9]{9}[v|V]|[0-9]{12}`
}
string value;
};

@test:Config {}
function StringConstraintPatternOnRecordFieldSuccess() {
StringConstraintPatternOnRecordField typ = {value: "964537342v"};
StringConstraintPatternOnRecordField|error validation = validate(typ);
if validation is error {
test:assertFail("Unexpected error found.");
}
}

@test:Config {}
function StringConstraintPatternOnRecordFieldFailure() {
StringConstraintPatternOnRecordField typ = {value: "9b4s37342v"};
StringConstraintPatternOnRecordField|error validation = validate(typ);
if validation is error {
test:assertEquals(validation.message(), "Validation failed for '$.value:pattern' constraint(s).");
} else {
test:assertFail("Expected error not found.");
}
}

type StringConstraintOnRecordField record {
@String {
minLength: 6,
maxLength: 12
maxLength: 12,
pattern: re `[a-z0-9](_?[a-z0-9])+`
}
string value;
};

@test:Config {}
isolated function testStringConstraintOnRecordFieldSuccess1() {
StringConstraintOnRecordField rec = {value: "b@!s3cr3t"};
StringConstraintOnRecordField rec = {value: "6a1_s3cr3t"};
StringConstraintOnRecordField|error validation = validate(rec);
if validation is error {
test:assertFail("Unexpected error found.");
Expand Down Expand Up @@ -197,3 +225,25 @@ isolated function testStringConstraintOnRecordFieldFailure2() {
test:assertFail("Expected error not found.");
}
}

@test:Config {}
isolated function testStringConstraintOnRecordFieldFailure3() {
StringConstraintOnRecordField rec = {value: "_6a1_u5er"};
StringConstraintOnRecordField|error validation = validate(rec);
if validation is error {
test:assertEquals(validation.message(), "Validation failed for '$.value:pattern' constraint(s).");
} else {
test:assertFail("Expected error not found.");
}
}

@test:Config {}
isolated function testStringConstraintOnRecordFieldFailure4() {
StringConstraintOnRecordField rec = {value: "_6a1_s3cr3t_u5er"};
StringConstraintOnRecordField|error validation = validate(rec);
if validation is error {
test:assertEquals(validation.message(), "Validation failed for '$.value:maxLength','$.value:pattern' constraint(s).");
} else {
test:assertFail("Expected error not found.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,37 @@ isolated function testStringConstraintMaxLengthOnTypeAsRecordFieldFailure() {
}
}

type StringConstraintPatternOnTypeAsRecordField record {
StringConstraintPatternOnType value;
};

@test:Config {}
function StringConstraintPatternOnTypeAsRecordFieldSuccess() {
StringConstraintPatternOnTypeAsRecordField typ = {value: "199645307342"};
StringConstraintPatternOnTypeAsRecordField|error validation = validate(typ);
if validation is error {
test:assertFail("Unexpected error found.");
}
}

@test:Config {}
function StringConstraintPatternOnTypeAsRecordFieldFailure() {
StringConstraintPatternOnTypeAsRecordField typ = {value: "199b453o7342"};
StringConstraintPatternOnTypeAsRecordField|error validation = validate(typ);
if validation is error {
test:assertEquals(validation.message(), "Validation failed for '$.value:pattern' constraint(s).");
} else {
test:assertFail("Expected error not found.");
}
}

type StringConstraintOnTypeAsRecordField record {
StringConstraintOnType value;
};

@test:Config {}
isolated function testStringConstraintOnTypeAsRecordFieldSuccess1() {
StringConstraintOnTypeAsRecordField rec = {value: "b@!s3cr3t"};
StringConstraintOnTypeAsRecordField rec = {value: "6a1_s3cr3t"};
StringConstraintOnTypeAsRecordField|error validation = validate(rec);
if validation is error {
test:assertFail("Unexpected error found.");
Expand Down Expand Up @@ -184,3 +208,26 @@ isolated function testStringConstraintOnTypeAsRecordFieldFailure2() {
test:assertFail("Expected error not found.");
}
}

@test:Config {}
isolated function testStringConstraintOnTypeAsRecordFieldFailure3() {
StringConstraintOnTypeAsRecordField rec = {value: "_6a1_u5er"};
StringConstraintOnTypeAsRecordField|error validation = validate(rec);
if validation is error {
test:assertEquals(validation.message(), "Validation failed for '$.value:pattern' constraint(s).");
} else {
test:assertFail("Expected error not found.");
}
}

@test:Config {}
isolated function testStringConstraintOnTypeAsRecordFieldFailure4() {
StringConstraintOnTypeAsRecordField rec = {value: "_6a1_s3cr3t_u5er"};
StringConstraintOnTypeAsRecordField|error validation = validate(rec);
if validation is error {
test:assertEquals(validation.message(), "Validation failed for '$.value:maxLength','$.value:pattern' constraint(s).");
} else {
test:assertFail("Expected error not found.");
}
}

72 changes: 70 additions & 2 deletions ballerina/tests/string_constraint_on_type_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,61 @@ isolated function testStringConstraintMaxLengthOnTypeFailure() {
}
}

@String {
pattern: re `[0-9]{9}[v|V]|[0-9]{12}`
}
type StringConstraintPatternOnType string;

@test:Config {}
function testStringConstraintPatternOnTypeSuccess1() {
StringConstraintPatternOnType typ = "964537342V";
StringConstraintPatternOnType|error validation = validate(typ);
if validation is error {
test:assertFail("Unexpected error found.");
}
}

@test:Config {}
function testStringConstraintPatternOnTypeSuccess2() {
StringConstraintPatternOnType typ = "199645370342";
StringConstraintPatternOnType|error validation = validate(typ);
if validation is error {
test:assertFail("Unexpected error found.");
}
}

@test:Config {}
function testStringConstraintPatternOnTypeFailure1() {
StringConstraintPatternOnType typ = "19964537034234";
StringConstraintPatternOnType|error validation = validate(typ);
if validation is error {
test:assertEquals(validation.message(), "Validation failed for '$:pattern' constraint(s).");
} else {
test:assertFail("Expected error not found.");
}
}

@test:Config {}
function testStringConstraintPatternOnTypeFailure2() {
StringConstraintPatternOnType typ = "199645345A";
StringConstraintPatternOnType|error validation = validate(typ);
if validation is error {
test:assertEquals(validation.message(), "Validation failed for '$:pattern' constraint(s).");
} else {
test:assertFail("Expected error not found.");
}
}

@String {
minLength: 6,
maxLength: 12
maxLength: 12,
pattern: re `[a-z0-9](_?[a-z0-9])+`
}
type StringConstraintOnType string;

@test:Config {}
isolated function testStringConstraintOnTypeSuccess1() {
StringConstraintOnType typ = "b@!s3cr3t";
StringConstraintOnType typ = "6a1_s3cr3t";
StringConstraintOnType|error validation = validate(typ);
if validation is error {
test:assertFail("Unexpected error found.");
Expand Down Expand Up @@ -187,3 +233,25 @@ isolated function testStringConstraintOnTypeFailure2() {
test:assertFail("Expected error not found.");
}
}

@test:Config {}
isolated function testStringConstraintOnTypeFailure3() {
StringConstraintOnType typ = "_6a1_u5er";
StringConstraintOnType|error validation = validate(typ);
if validation is error {
test:assertEquals(validation.message(), "Validation failed for '$:pattern' constraint(s).");
} else {
test:assertFail("Expected error not found.");
}
}

@test:Config {}
isolated function testStringConstraintOnTypeFailure4() {
StringConstraintOnType typ = "_6a1_s3cr3t_u5er";
StringConstraintOnType|error validation = validate(typ);
if validation is error {
test:assertEquals(validation.message(), "Validation failed for '$:maxLength','$:pattern' constraint(s).");
} else {
test:assertFail("Expected error not found.");
}
}
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Fixed
- [Fix unexpected errors due to custom annotations](https://github.com/ballerina-platform/ballerina-standard-library/issues/3817)

### Added
- [Add `@pattern` constraint on string types](https://github.com/ballerina-platform/ballerina-standard-library/issues/3179)

## [1.0.1] - 2022-11-29

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,31 @@

import ballerina/constraint;

string:RegExp regExp = re `[0-9]*`;

@constraint:String {
length: 10,
pattern: regExp
}
type PhoneNumber string;

@constraint:String {
pattern: re `^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})*$`
}
type MailAddress string;

type User record {|
string name;
int age;
@constraint:String {
pattern: re `([0-9]{9}[v|V]|[0-9]{12})`
}
string nic;
string address;
PhoneNumber contactNumber;
MailAddress email;
|};

type Foo record {
@constraint:String {
length: 10
Expand Down
Loading

0 comments on commit c28d998

Please sign in to comment.