thrift
thrift copied to clipboard
THRIFT-5423: Support go parameter validation in IDL
Support parameter validation, only go validation generator is supported now. Users can define validation rule for struct in Thrift file, then the generator will generate validation code, for example, go validation generator will generate IsValid() method.
Below is an example:
enum MapKey {
A, B, C, D, E, F
}
struct Example {
1: string Message (vt.min_size = "30") // length of Message should be greater than or equal to 30
2: i32 ID (vt.ge = "10000") // ID must be greater than or euqal to 10000
3: list<double> Values (vt.elem.gt = "0.25") // element of Values must be greater than 0.25
4: map<MapKey, string> KeyValues (vt.key.defined_only = "true") // value of KeyValues'key must be defined in MapKey
}
Feature Matrix:
prefix vt, short for "validation"
Numeric(i8/i16/i32/i64/double):
| Rule | |
|---|---|
| vt.const | must be specified value |
| vt.lt | less than the specified value |
| vt.le | less than or equal to specified value |
| vt.gt | greater than the specified value |
| vt.ge | greater than or equal to specified value |
| vt.in | must be in specified values |
| vt.not_in | must not be in specified values |
Bool:
| Rule | |
|---|---|
| vt.const | must be specified value |
String/Binary:
| Rule | |
|---|---|
| vt.const | must be specified value |
| vt.pattern | regexp pattern |
| vt.prefix | prefix must be specified value |
| vt.suffix | suffix must be specified value |
| vt.contains | must contain specified value |
| vt.not_contains | must not contain specified value |
| vt.min_size | min size |
| vt.max_size | max size |
Bool:
| Rule | |
|---|---|
| vt.const | must be specified value |
Enum:
| Rule | |
|---|---|
| vt.const | must be specified value |
| vt.defined_only | must be defined value |
Set/List:
| Rule | |
|---|---|
| vt.min_size | min size |
| vt.max_size | max size |
| vt.elem | rule for list element |
Map:
| Rule | |
|---|---|
| vt.min_size | min size |
| vt.max_size | max size |
| vt.key | rule for map key |
| vt.value | rule for map value |
Struct
| Rule | |
|---|---|
| vt.skip | skip struct recursive validation |
Special Value:
- Field Reference. We can use another field as a validation value.
- Validation Function. We can use those functions to provide extensive validation ability.
Field Reference Example:
struct Example {
1: string StringFoo (vt.max_size = "$MaxStringSize")
2: i32 MaxStringSize
}
Validation Function:
struct Example {
1: string MaxString
2: list<string> StringList (vt.elem.max_size = "@len($MaxString)")
}
for now, only len function is available.
- [x] Did you create an Apache Jira ticket? (not required for trivial changes)
- [x] If a ticket exists: Does your pull request title follow the pattern "THRIFT-NNNN: describe my issue"?
- [x] Did you squash your changes to a single commit? (not required, but preferred)
- [x] Did you do your best to avoid breaking changes? If one was needed, did you label the Jira ticket with "Breaking-Change"?
- [ ] If your change does not involve any code, include
[skip ci]anywhere in the commit message to free up build resources.
Prefix "vt" stands for validation
That would have not been my first guess. Mmmh. Ok.
defiend_only
The good news is In the code it is spelled correctly :-)
no_sparse map value must be non-nil pointer
Not sure if I can agree. "Sparse" in the context of memory management means usually something else.
Bonus question: I only skimmed the code so I probably overlooked it, but when and where is it called?
Thanks for the quick reply.
Prefix "vt" stands for validation
That would have not been my first guess. Mmmh. Ok.
Yes, 'vt' stands for validation, considering that 'vt' might make fewer conflicts.
defiend_only
The good news is In the code it is spelt correctly :-)
I have fixed the comment.
no_sparse map value must be non-nil pointer
Not sure if I can agree. "Sparse" in the context of memory management means usually something else.
Bonus question: I only skimmed the code so I probably overlooked it, but when and where is it called?
no_sparse is designed for go, since like map<string, StructFoo> is a pretty common data structure in web service, and a nil value could lead to an unexpected result (an empty struct) after deserilization. But not sure it's a common problem for all languages, so no_sparse is not implemented. I will remove it from the feature matrix.
Re "sparse": It was not about the feature, but about terminology. A sparse array is an array where most values are unused (e.g. NULL/nil), and the most significant problem about it is to store it with a minimum amount of memory, e.g. imagine an 2-sided matrix with 5 mio by 5 mio possible combinations, where only a few thousand are really used. The challenge here is to get it small AND still fast. With that in mind, a map or dictionary is by definition an efficient, sparse data structure with something in the range of O(1) for reads. Hence, "sparse map" makes not much sense, plus what you really mean is sth completely different: The entry is used, only the value happens to be null. An existing null entry and a non-existing entry are two different things.
PS: Is that IsValid() called on reads and writes or only at one of these? Or do I have to invoke it explicitly by calling IsValid()?
Re "sparse": It was not about the feature, but about terminology. A sparse array is an array where most values are unused (e.g. NULL/nil), and the most significant problem about it is to store it with a minimum amount of memory, e.g. imagine an 2-sided matrix with 5 mio by 5 mio possible combinations, where only a few thousand are really used. The challenge here is to get it small AND still fast. With that in mind, a map or dictionary is by definition an efficient, sparse data structure with something in the range of O(1) for reads. Hence, "sparse map" makes not much sense, plus what you really mean is sth completely different: The entry is used, only the value happens to be null. An existing null entry and a non-existing entry are two different things.
Thanks for explanation.
PS: Is that IsValid() called on reads and writes or only at one of these? Or do I have to invoke it explicitly by calling IsValid()?
For now, go generator only provides 'generate_validator' flag to generate IsValid() method, so we have to call IsValid() to validate the structure date.
@Jens-G Could you help review the code or assign it to someone else for review?
vt.in: must be in specified values vt.not_in: must not be in specified values
it's not clear to me whether that is an enumeration (e.g. vt.in = "[1, 2, 4, 8]" or a range (e.g. vt.in = "[1, 4]"), and if it's a range, are ends inclusive or exclusive?
I think it would help if you can include the markdown file you attached to the JIRA ticket into this PR so I can review the spec first (and it will be easier to give inline feedback to the spec there), before I review the implementation code.
vt.in: must be in specified values vt.not_in: must not be in specified values
it's not clear to me whether that is an enumeration (e.g.
vt.in = "[1, 2, 4, 8]"or a range (e.g.vt.in = "[1, 4]"), and if it's a range, are ends inclusive or exclusive?I think it would help if you can include the markdown file you attached to the JIRA ticket into this PR so I can review the spec first (and it will be easier to give inline feedback to the spec there), before I review the implementation code.
Sorry for not getting back to you sooner. Value of 'vt.in' must be specified,it works like below
3: i8 Byte0 = 1 (vt.in = "0", vt.in = "1", vt.in = "2", vt.not_in = "3", vt.not_in = "4", vt.not_in = "5")
We update thrift-parameter-validation-proposal, and put it in doc/proposal/thrift-parameter-validation-proposal.md. Thanks for your advice, and welcome to comment.
I add an explicit definition for commas between rules and also a reference for basic regular expression.
@fishy Please take a look at this pr, we are looking forward to any progress related to it. : ) We, together with @simon0-o , are very glad to contribute to Thrift community continuously.
please also resolve the merge conflicts.
it would also be helpful (since the review on the spec/proposal part is mostly done) to paste the compiler generated go code into the pr so it's easier to review the code.
@fishy hi, if you have any progress or new comments related to this pr, please let us know.
@GuangmingLuo none of the travis test passed, it doesn't look the compiler code compiles, so maybe fix that first?
also in my previous comment I said that:
it would also be helpful (since the review on the spec/proposal part is mostly done) to paste the compiler generated go code into the pr so it's easier to review the code.
and I don't see anyone pasting any generated validator go code into the comments.
Generated Code Examples: ValidateTest.thrift
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
*/
namespace go validatetest
enum EnumFoo {
e1
e2
}
struct Foo {
1: bool Bool
}
struct BasicTest {
1: bool Bool0 = true (vt.const = "true")
2: optional bool Bool1 (vt.const = "true")
3: i8 Byte0 = 1 (vt.lt = "2", vt.le = "2", vt.gt = "0", vt.ge = "0", vt.in = "[0, 1, 2]", vt.not_in = "[3, 4, 5]")
4: optional i8 Byte1 (vt.lt = "1", vt.le = "1", vt.gt = "-1", vt.ge = "-1", vt.in = "[-1, 0, 1]", vt.not_in = "[1, 2, 3]")
5: double Double0 = 1.0 (vt.lt = "2.0", vt.le = "2.0", vt.gt = "0", vt.ge = "0", vt.in = "[0, 1.0, 2.0]", vt.not_in = "[3.0, 4.0, 5.0]")
6: optional double Double1 (vt.lt = "2.0", vt.le = "2.0", vt.gt = "0", vt.ge = "0", vt.in = "[0, 1.0, 2.0]", vt.not_in = "[3.0, 4.0, 5.0]")
7: string String0 = "my const string" (vt.const = "my const string", vt.min_size = "0", vt.max_size = "100", vt.pattern = ".*", vt.prefix = "my", vt.suffix = "string", vt.contains = "const", vt.not_contains = "oh")
8: optional string String1 (vt.const = "my const string", vt.min_size = "0", vt.max_size = "100", vt.pattern = ".*", vt.prefix = "my", vt.suffix = "string", vt.contains = "const", vt.not_contains = "oh")
9: binary Binary0 = "my const string" (vt.const = "my const string", vt.min_size = "0", vt.max_size = "100", vt.pattern = ".*", vt.prefix = "my", vt.suffix = "string", vt.contains = "const", vt.not_contains = "oh")
10: optional binary Binary1 = "my const string" (vt.const = "my const string", vt.min_size = "0", vt.max_size = "100", vt.pattern = ".*", vt.prefix = "my", vt.suffix = "string", vt.contains = "const", vt.not_contains = "oh")
11: map<string, string> Map0 (vt.min_size = "0", vt.max_size = "10", vt.key.min_size = "0", vt.key.max_size = "10", vt.value.min_size = "0", vt.value.max_size = "10")
12: optional map<string, string> Map1 (vt.min_size = "0", vt.max_size = "10", vt.key.min_size = "0", vt.key.max_size = "10", vt.value.min_size = "0", vt.value.max_size = "10")
13: set<string> Set0 (vt.min_size = "0", vt.max_size = "10", vt.elem.min_size = "5")
14: optional set<string> Set1 (vt.min_size = "0", vt.max_size = "10", vt.elem.min_size = "5")
15: EnumFoo Enum0 = EnumFoo.e2 (vt.in = "[EnumFoo.e2]", vt.defined_only = "true")
16: optional EnumFoo Enum1 (vt.in = "[EnumFoo.e1]", vt.defined_only = "true")
17: Foo Struct0 (vt.skip = "true")
18: optional Foo Struct1 (vt.skip = "true")
19: i8 Byte2 = 1 (vt.in = "1", vt.not_in = "2")
20: double Double2 = 3.0 (vt.in = "3.0", vt.not_in = "4.0")
21: EnumFoo Enum2 = EnumFoo.e2 (vt.in = "EnumFoo.e2", vt.not_in = "EnumFoo.e1")
}
struct FieldReferenceTest {
1: bool Bool0 (vt.const = "$Bool2")
2: optional bool Bool1 (vt.const = "$Bool2")
3: i8 Byte0 = 10 (vt.lt = "$Byte4", vt.le = "$Byte4", vt.gt = "$Byte2", vt.ge = "$Byte2", vt.in = "[$Byte2, $Byte3, $Byte4]", vt.not_in = "[$Byte2, $Byte4]")
4: optional i8 Byte1 (vt.lt = "$Byte4", vt.le = "$Byte4", vt.gt = "$Byte2", vt.ge = "$Byte2", vt.in = "[$Byte2, $Byte3, $Byte4]", vt.not_in = "[$Byte2, $Byte4]")
5: double Double0 = 10.0 (vt.lt = "$Double4", vt.le = "$Double4", vt.gt = "$Double2", vt.ge = "$Double2", vt.in = "[$Double2, $Double3, $Double4]", vt.not_in = "[$Double2, $Double4]")
6: optional double Double1 (vt.lt = "$Double4", vt.le = "$Double4", vt.gt = "$Double2", vt.ge = "$Double2", vt.in = "[$Double2, $Double3, $Double4]", vt.not_in = "[$Double2, $Double4]")
7: string String0 = "my string" (vt.const = "$String2", vt.min_size = "$Byte2", vt.max_size = "$Byte3", vt.pattern = "$String4", vt.prefix = "$String2", vt.suffix = "$String2", vt.contains = "$String2", vt.not_contains = "$String3")
8: optional string String1 (vt.const = "$String2", vt.min_size = "$Byte2", vt.max_size = "$Byte3", vt.pattern = "$String4", vt.prefix = "$String2", vt.suffix = "$String2", vt.contains = "$String2", vt.not_contains = "$String3")
9: binary Binary0 = "my binary" (vt.const = "$Binary2", vt.min_size = "$Byte2", vt.max_size = "$Byte3", vt.pattern = "$Binary4", vt.prefix = "$Binary2", vt.suffix = "$Binary2", vt.contains = "$Binary2", vt.not_contains = "$Binary3")
10: optional binary Binary1 = "my binary" (vt.const = "$Binary2", vt.min_size = "$Byte2", vt.max_size = "$Byte3", vt.pattern = "$Binary4", vt.prefix = "$Binary2", vt.suffix = "$Binary2", vt.contains = "$Binary2", vt.not_contains = "$Binary3")
11: map<string, string> Map0 (vt.min_size = "$Byte2", vt.max_size = "$MaxSize", vt.key.min_size = "$Byte2", vt.key.max_size = "$MaxSize", vt.value.min_size = "$Byte2", vt.value.max_size = "$MaxSize")
12: optional map<string, string> Map1 (vt.min_size = "$Byte2", vt.max_size = "$MaxSize", vt.key.min_size = "$Byte2", vt.key.max_size = "$MaxSize", vt.value.min_size = "$Byte2", vt.value.max_size = "$MaxSize")
13: list<string> List0 (vt.min_size = "$Byte2", vt.max_size = "$MaxSize", vt.elem.min_size = "$Byte2", vt.elem.max_size = "$MaxSize")
14: optional list<string> List1 (vt.min_size = "$Byte2", vt.max_size = "$MaxSize", vt.elem.min_size = "$Byte2", vt.elem.max_size = "$MaxSize")
15: set<string> Set0 (vt.min_size = "$Byte2", vt.max_size = "$MaxSize", vt.elem.min_size = "$Byte2", vt.elem.max_size = "$MaxSize")
16: optional set<string> Set1 (vt.min_size = "$Byte2", vt.max_size = "$MaxSize", vt.elem.min_size = "$Byte2", vt.elem.max_size = "$MaxSize")
17: bool Bool2 = false
18: i8 Byte2 = 0
19: i8 Byte3 = 10
20: i8 Byte4 = 20
21: double Double2 = 0
22: double Double3 = 10.0
23: double Double4 = 20.0
24: string String2 = "my string"
25: string String3 = "other string"
26: string String4 = ".*"
27: binary Binary2 = "my binary"
28: binary Binary3 = "other binary"
29: binary Binary4 = ".*"
30: i64 MaxSize = 10
}
struct ValidationFunctionTest {
1: string StringFoo
2: i64 StringLength (vt.in = "[@len($StringFoo)]")
}
struct AnnotationCompatiableTest {
1: bool Bool0 = true (vt.const = "true", go.tag = 'json:"bool0"')
2: i8 Byte0 = 1 (vt.lt = "2", go.tag = 'json:"byte0"')
3: double Double0 = 1.0 (vt.lt = "2.0", go.tag = 'json:"double0"')
4: string String0 = "my const string" (vt.const = "my const string", go.tag = 'json:"string0"')
5: binary Binary0 = "my const string" (vt.const = "my const string", go.tag = 'json:"binary0"')
6: map<string, string> Map0 (vt.min_size = "0", go.tag = 'json:"map0"')
7: set<string> Set0 (vt.min_size = "0", go.tag = 'json:"set0"')
8: EnumFoo Enum0 = EnumFoo.e2 (vt.in = "[EnumFoo.e2]", go.tag = 'json:"enum0"')
9: Foo Struct0 (vt.skip = "true", go.tag = 'json:"struct0"')
}
Related Generated Code:
func (p *Foo) IsValid() error {
return nil
}
func (p *BasicTest) Validate() error {
if p.Bool0 != true{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Bool0 not valid, rule vt.const check failed")
}
if p.Bool1 != nil {
if *p.Bool1 != true{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Bool1 not valid, rule vt.const check failed")
}
}
if p.Byte0 >= 2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte0 not valid, rule vt.lt check failed")
}
if p.Byte0 > 2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte0 not valid, rule vt.le check failed")
}
if p.Byte0 <= 0{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte0 not valid, rule vt.gt check failed")
}
if p.Byte0 < 0{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte0 not valid, rule vt.ge check failed")
}
var _exist0 bool
_src0 := []int8{0, 1, 2}
for _, src := range _src0 {
if p.Byte0 == src {
_exist0 = true
break
}
}
if _exist0 == false {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte0 not valid, rule vt.in check failed")
}
_src1 := []int8{3, 4, 5}
for _, src := range _src1 {
if p.Byte0 == src {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte0 not valid, rule vt.not_in check failed")
}
}
if p.Byte1 != nil {
if *p.Byte1 >= 1{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte1 not valid, rule vt.lt check failed")
}
if *p.Byte1 > 1{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte1 not valid, rule vt.le check failed")
}
if *p.Byte1 <= -1{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte1 not valid, rule vt.gt check failed")
}
if *p.Byte1 < -1{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte1 not valid, rule vt.ge check failed")
}
var _exist1 bool
_src2 := []int8{-1, 0, 1}
for _, src := range _src2 {
if *p.Byte1 == src {
_exist1 = true
break
}
}
if _exist1 == false {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte1 not valid, rule vt.in check failed")
}
_src3 := []int8{1, 2, 3}
for _, src := range _src3 {
if *p.Byte1 == src {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte1 not valid, rule vt.not_in check failed")
}
}
}
if p.Double0 >= 2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double0 not valid, rule vt.lt check failed")
}
if p.Double0 > 2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double0 not valid, rule vt.le check failed")
}
if p.Double0 <= 0{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double0 not valid, rule vt.gt check failed")
}
if p.Double0 < 0{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double0 not valid, rule vt.ge check failed")
}
var _exist2 bool
_src4 := []float64{0, 1, 2}
for _, src := range _src4 {
if p.Double0 == src {
_exist2 = true
break
}
}
if _exist2 == false {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double0 not valid, rule vt.in check failed")
}
_src5 := []float64{3, 4, 5}
for _, src := range _src5 {
if p.Double0 == src {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double0 not valid, rule vt.not_in check failed")
}
}
if p.Double1 != nil {
if *p.Double1 >= 2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double1 not valid, rule vt.lt check failed")
}
if *p.Double1 > 2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double1 not valid, rule vt.le check failed")
}
if *p.Double1 <= 0{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double1 not valid, rule vt.gt check failed")
}
if *p.Double1 < 0{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double1 not valid, rule vt.ge check failed")
}
var _exist3 bool
_src6 := []float64{0, 1, 2}
for _, src := range _src6 {
if *p.Double1 == src {
_exist3 = true
break
}
}
if _exist3 == false {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double1 not valid, rule vt.in check failed")
}
_src7 := []float64{3, 4, 5}
for _, src := range _src7 {
if *p.Double1 == src {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double1 not valid, rule vt.not_in check failed")
}
}
}
if p.String0 != "my const string"{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String0 not valid, rule vt.const check failed")
}
if len(p.String0) < int(0){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String0 not valid, rule vt.min_size check failed")
}
if len(p.String0) > int(100){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String0 not valid, rule vt.max_size check failed")
}
if ok, _ := regexp.MatchString(p.String0,".*"); ok {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String0 not valid, rule vt.pattern check failed")
}
if !strings.HasPrefix(p.String0,"my"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String0 not valid, rule vt.prefix check failed")
}
if !strings.HasSuffix(p.String0,"string"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String0 not valid, rule vt.suffix check failed")
}
if !strings.Contains(p.String0,"const"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String0 not valid, rule vt.contains check failed")
}
if strings.Contains(p.String0,"oh"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String0 not valid, rule vt.not_contains check failed")
}
if p.String1 != nil {
if *p.String1 != "my const string"{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String1 not valid, rule vt.const check failed")
}
if len(*p.String1) < int(0){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String1 not valid, rule vt.min_size check failed")
}
if len(*p.String1) > int(100){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String1 not valid, rule vt.max_size check failed")
}
if ok, _ := regexp.MatchString(*p.String1,".*"); ok {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String1 not valid, rule vt.pattern check failed")
}
if !strings.HasPrefix(*p.String1,"my"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String1 not valid, rule vt.prefix check failed")
}
if !strings.HasSuffix(*p.String1,"string"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String1 not valid, rule vt.suffix check failed")
}
if !strings.Contains(*p.String1,"const"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String1 not valid, rule vt.contains check failed")
}
if strings.Contains(*p.String1,"oh"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.String1 not valid, rule vt.not_contains check failed")
}
}
_tgt0 := string(p.Binary0)
if _tgt0 != "my const string"{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary0 not valid, rule vt.const check failed")
}
if len(_tgt0) < int(0){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary0 not valid, rule vt.min_size check failed")
}
if len(_tgt0) > int(100){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary0 not valid, rule vt.max_size check failed")
}
if ok, _ := regexp.MatchString(_tgt0,".*"); ok {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary0 not valid, rule vt.pattern check failed")
}
if !strings.HasPrefix(_tgt0,"my"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary0 not valid, rule vt.prefix check failed")
}
if !strings.HasSuffix(_tgt0,"string"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary0 not valid, rule vt.suffix check failed")
}
if !strings.Contains(_tgt0,"const"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary0 not valid, rule vt.contains check failed")
}
if strings.Contains(_tgt0,"oh"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary0 not valid, rule vt.not_contains check failed")
}
_tgt1 := string(p.Binary1)
if _tgt1 != "my const string"{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary1 not valid, rule vt.const check failed")
}
if len(_tgt1) < int(0){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary1 not valid, rule vt.min_size check failed")
}
if len(_tgt1) > int(100){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary1 not valid, rule vt.max_size check failed")
}
if ok, _ := regexp.MatchString(_tgt1,".*"); ok {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary1 not valid, rule vt.pattern check failed")
}
if !strings.HasPrefix(_tgt1,"my"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary1 not valid, rule vt.prefix check failed")
}
if !strings.HasSuffix(_tgt1,"string"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary1 not valid, rule vt.suffix check failed")
}
if !strings.Contains(_tgt1,"const"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary1 not valid, rule vt.contains check failed")
}
if strings.Contains(_tgt1,"oh"){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Binary1 not valid, rule vt.not_contains check failed")
}
if len(p.Map0) < 0{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Map0 not valid, rule vt.min_size check failed")
}
if len(p.Map0) > 10{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Map0 not valid, rule vt.max_size check failed")
}
for _key0 := range p.Map0 {
if len(_key0) < int(0){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Map0.key not valid, rule vt.min_size check failed")
}
}
for _key1 := range p.Map0 {
if len(_key1) > int(10){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Map0.key not valid, rule vt.max_size check failed")
}
}
for _, _value0 := range p.Map0 {
if len(_value0) < int(0){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Map0.value not valid, rule vt.min_size check failed")
}
}
for _, _value1 := range p.Map0 {
if len(_value1) > int(10){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Map0.value not valid, rule vt.max_size check failed")
}
}
if len(p.Map1) < 0{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Map1 not valid, rule vt.min_size check failed")
}
if len(p.Map1) > 10{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Map1 not valid, rule vt.max_size check failed")
}
for _key2 := range p.Map1 {
if len(_key2) < int(0){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Map1.key not valid, rule vt.min_size check failed")
}
}
for _key3 := range p.Map1 {
if len(_key3) > int(10){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Map1.key not valid, rule vt.max_size check failed")
}
}
for _, _value2 := range p.Map1 {
if len(_value2) < int(0){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Map1.value not valid, rule vt.min_size check failed")
}
}
for _, _value3 := range p.Map1 {
if len(_value3) > int(10){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Map1.value not valid, rule vt.max_size check failed")
}
}
if len(p.Set0) < 0{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Set0 not valid, rule vt.min_size check failed")
}
if len(p.Set0) > 10{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Set0 not valid, rule vt.max_size check failed")
}
for i := 0; i < len(p.Set0);i++ {
_elem0 := p.Set0[i]
if len(_elem0) < int(5){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Set0.elem not valid, rule vt.min_size check failed")
}
}
if len(p.Set1) < 0{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Set1 not valid, rule vt.min_size check failed")
}
if len(p.Set1) > 10{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Set1 not valid, rule vt.max_size check failed")
}
for i := 0; i < len(p.Set1);i++ {
_elem1 := p.Set1[i]
if len(_elem1) < int(5){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Set1.elem not valid, rule vt.min_size check failed")
}
}
if (p.Enum0).String() == "<UNSET>" {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Enum0 not valid, rule vt.defined_only check failed")
}
if int64(p.Enum0) != int64(1) {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Enum0 not valid, rule vt.in check failed")
}
if p.Enum1 != nil {
if (*p.Enum1).String() == "<UNSET>" {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Enum1 not valid, rule vt.defined_only check failed")
}
if int64(*p.Enum1) != int64(0) {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Enum1 not valid, rule vt.in check failed")
}
}
if p.Byte2 != 1{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte2 not valid, rule vt.in check failed")
}
if p.Byte2 == 2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Byte2 not valid, rule vt.not_in check failed")
}
if p.Double2 != 3{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double2 not valid, rule vt.in check failed")
}
if p.Double2 == 4{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Double2 not valid, rule vt.not_in check failed")
}
if int64(p.Enum2) != int64(1) {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Enum2 not valid, rule vt.in check failed")
}
if int64(p.Enum2) == int64(0) {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "BasicTest.Enum2 not valid, rule vt.not_in check failed")
}
return nil
}
func (p *FieldReferenceTest) Validate() error {
if p.Bool0 != p.Bool2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Bool0 not valid, rule vt.const check failed")
}
if p.Bool1 != nil {
if *p.Bool1 != p.Bool2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Bool1 not valid, rule vt.const check failed")
}
}
if p.Byte0 >= p.Byte4{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Byte0 not valid, rule vt.lt check failed")
}
if p.Byte0 > p.Byte4{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Byte0 not valid, rule vt.le check failed")
}
if p.Byte0 <= p.Byte2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Byte0 not valid, rule vt.gt check failed")
}
if p.Byte0 < p.Byte2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Byte0 not valid, rule vt.ge check failed")
}
var _exist0 bool
_src0 := []int8{p.Byte2, p.Byte3, p.Byte4}
for _, src := range _src0 {
if p.Byte0 == src {
_exist0 = true
break
}
}
if _exist0 == false {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Byte0 not valid, rule vt.in check failed")
}
_src1 := []int8{p.Byte2, p.Byte4}
for _, src := range _src1 {
if p.Byte0 == src {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Byte0 not valid, rule vt.not_in check failed")
}
}
if p.Byte1 != nil {
if *p.Byte1 >= p.Byte4{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Byte1 not valid, rule vt.lt check failed")
}
if *p.Byte1 > p.Byte4{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Byte1 not valid, rule vt.le check failed")
}
if *p.Byte1 <= p.Byte2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Byte1 not valid, rule vt.gt check failed")
}
if *p.Byte1 < p.Byte2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Byte1 not valid, rule vt.ge check failed")
}
var _exist1 bool
_src2 := []int8{p.Byte2, p.Byte3, p.Byte4}
for _, src := range _src2 {
if *p.Byte1 == src {
_exist1 = true
break
}
}
if _exist1 == false {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Byte1 not valid, rule vt.in check failed")
}
_src3 := []int8{p.Byte2, p.Byte4}
for _, src := range _src3 {
if *p.Byte1 == src {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Byte1 not valid, rule vt.not_in check failed")
}
}
}
if p.Double0 >= p.Double4{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Double0 not valid, rule vt.lt check failed")
}
if p.Double0 > p.Double4{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Double0 not valid, rule vt.le check failed")
}
if p.Double0 <= p.Double2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Double0 not valid, rule vt.gt check failed")
}
if p.Double0 < p.Double2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Double0 not valid, rule vt.ge check failed")
}
var _exist2 bool
_src4 := []float64{p.Double2, p.Double3, p.Double4}
for _, src := range _src4 {
if p.Double0 == src {
_exist2 = true
break
}
}
if _exist2 == false {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Double0 not valid, rule vt.in check failed")
}
_src5 := []float64{p.Double2, p.Double4}
for _, src := range _src5 {
if p.Double0 == src {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Double0 not valid, rule vt.not_in check failed")
}
}
if p.Double1 != nil {
if *p.Double1 >= p.Double4{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Double1 not valid, rule vt.lt check failed")
}
if *p.Double1 > p.Double4{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Double1 not valid, rule vt.le check failed")
}
if *p.Double1 <= p.Double2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Double1 not valid, rule vt.gt check failed")
}
if *p.Double1 < p.Double2{
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Double1 not valid, rule vt.ge check failed")
}
var _exist3 bool
_src6 := []float64{p.Double2, p.Double3, p.Double4}
for _, src := range _src6 {
if *p.Double1 == src {
_exist3 = true
break
}
}
if _exist3 == false {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Double1 not valid, rule vt.in check failed")
}
_src7 := []float64{p.Double2, p.Double4}
for _, src := range _src7 {
if *p.Double1 == src {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Double1 not valid, rule vt.not_in check failed")
}
}
}
if p.String0 != string(p.String2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String0 not valid, rule vt.const check failed")
}
if len(p.String0) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String0 not valid, rule vt.min_size check failed")
}
if len(p.String0) > int(p.Byte3){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String0 not valid, rule vt.max_size check failed")
}
if ok, _ := regexp.MatchString(p.String0,string(p.String4)); ok {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String0 not valid, rule vt.pattern check failed")
}
if !strings.HasPrefix(p.String0,string(p.String2)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String0 not valid, rule vt.prefix check failed")
}
if !strings.HasSuffix(p.String0,string(p.String2)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String0 not valid, rule vt.suffix check failed")
}
if !strings.Contains(p.String0,string(p.String2)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String0 not valid, rule vt.contains check failed")
}
if strings.Contains(p.String0,string(p.String3)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String0 not valid, rule vt.not_contains check failed")
}
if p.String1 != nil {
if *p.String1 != string(p.String2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String1 not valid, rule vt.const check failed")
}
if len(*p.String1) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String1 not valid, rule vt.min_size check failed")
}
if len(*p.String1) > int(p.Byte3){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String1 not valid, rule vt.max_size check failed")
}
if ok, _ := regexp.MatchString(*p.String1,string(p.String4)); ok {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String1 not valid, rule vt.pattern check failed")
}
if !strings.HasPrefix(*p.String1,string(p.String2)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String1 not valid, rule vt.prefix check failed")
}
if !strings.HasSuffix(*p.String1,string(p.String2)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String1 not valid, rule vt.suffix check failed")
}
if !strings.Contains(*p.String1,string(p.String2)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String1 not valid, rule vt.contains check failed")
}
if strings.Contains(*p.String1,string(p.String3)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.String1 not valid, rule vt.not_contains check failed")
}
}
_tgt0 := string(p.Binary0)
if _tgt0 != string(p.Binary2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary0 not valid, rule vt.const check failed")
}
if len(_tgt0) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary0 not valid, rule vt.min_size check failed")
}
if len(_tgt0) > int(p.Byte3){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary0 not valid, rule vt.max_size check failed")
}
if ok, _ := regexp.MatchString(_tgt0,string(p.Binary4)); ok {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary0 not valid, rule vt.pattern check failed")
}
if !strings.HasPrefix(_tgt0,string(p.Binary2)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary0 not valid, rule vt.prefix check failed")
}
if !strings.HasSuffix(_tgt0,string(p.Binary2)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary0 not valid, rule vt.suffix check failed")
}
if !strings.Contains(_tgt0,string(p.Binary2)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary0 not valid, rule vt.contains check failed")
}
if strings.Contains(_tgt0,string(p.Binary3)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary0 not valid, rule vt.not_contains check failed")
}
_tgt1 := string(p.Binary1)
if _tgt1 != string(p.Binary2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary1 not valid, rule vt.const check failed")
}
if len(_tgt1) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary1 not valid, rule vt.min_size check failed")
}
if len(_tgt1) > int(p.Byte3){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary1 not valid, rule vt.max_size check failed")
}
if ok, _ := regexp.MatchString(_tgt1,string(p.Binary4)); ok {
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary1 not valid, rule vt.pattern check failed")
}
if !strings.HasPrefix(_tgt1,string(p.Binary2)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary1 not valid, rule vt.prefix check failed")
}
if !strings.HasSuffix(_tgt1,string(p.Binary2)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary1 not valid, rule vt.suffix check failed")
}
if !strings.Contains(_tgt1,string(p.Binary2)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary1 not valid, rule vt.contains check failed")
}
if strings.Contains(_tgt1,string(p.Binary3)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Binary1 not valid, rule vt.not_contains check failed")
}
if len(p.Map0) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Map0 not valid, rule vt.min_size check failed")
}
if len(p.Map0) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Map0 not valid, rule vt.max_size check failed")
}
for _key0 := range p.Map0 {
if len(_key0) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Map0.key not valid, rule vt.min_size check failed")
}
}
for _key1 := range p.Map0 {
if len(_key1) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Map0.key not valid, rule vt.max_size check failed")
}
}
for _, _value0 := range p.Map0 {
if len(_value0) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Map0.value not valid, rule vt.min_size check failed")
}
}
for _, _value1 := range p.Map0 {
if len(_value1) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Map0.value not valid, rule vt.max_size check failed")
}
}
if len(p.Map1) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Map1 not valid, rule vt.min_size check failed")
}
if len(p.Map1) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Map1 not valid, rule vt.max_size check failed")
}
for _key2 := range p.Map1 {
if len(_key2) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Map1.key not valid, rule vt.min_size check failed")
}
}
for _key3 := range p.Map1 {
if len(_key3) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Map1.key not valid, rule vt.max_size check failed")
}
}
for _, _value2 := range p.Map1 {
if len(_value2) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Map1.value not valid, rule vt.min_size check failed")
}
}
for _, _value3 := range p.Map1 {
if len(_value3) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Map1.value not valid, rule vt.max_size check failed")
}
}
if len(p.List0) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.List0 not valid, rule vt.min_size check failed")
}
if len(p.List0) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.List0 not valid, rule vt.max_size check failed")
}
for i := 0; i < len(p.List0);i++ {
_elem0 := p.List0[i]
if len(_elem0) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.List0.elem not valid, rule vt.min_size check failed")
}
}
for i := 0; i < len(p.List0);i++ {
_elem1 := p.List0[i]
if len(_elem1) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.List0.elem not valid, rule vt.max_size check failed")
}
}
if len(p.List1) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.List1 not valid, rule vt.min_size check failed")
}
if len(p.List1) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.List1 not valid, rule vt.max_size check failed")
}
for i := 0; i < len(p.List1);i++ {
_elem2 := p.List1[i]
if len(_elem2) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.List1.elem not valid, rule vt.min_size check failed")
}
}
for i := 0; i < len(p.List1);i++ {
_elem3 := p.List1[i]
if len(_elem3) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.List1.elem not valid, rule vt.max_size check failed")
}
}
if len(p.Set0) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Set0 not valid, rule vt.min_size check failed")
}
if len(p.Set0) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Set0 not valid, rule vt.max_size check failed")
}
for i := 0; i < len(p.Set0);i++ {
_elem4 := p.Set0[i]
if len(_elem4) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Set0.elem not valid, rule vt.min_size check failed")
}
}
for i := 0; i < len(p.Set0);i++ {
_elem5 := p.Set0[i]
if len(_elem5) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Set0.elem not valid, rule vt.max_size check failed")
}
}
if len(p.Set1) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Set1 not valid, rule vt.min_size check failed")
}
if len(p.Set1) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Set1 not valid, rule vt.max_size check failed")
}
for i := 0; i < len(p.Set1);i++ {
_elem6 := p.Set1[i]
if len(_elem6) < int(p.Byte2){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Set1.elem not valid, rule vt.min_size check failed")
}
}
for i := 0; i < len(p.Set1);i++ {
_elem7 := p.Set1[i]
if len(_elem7) > int(p.MaxSize){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "FieldReferenceTest.Set1.elem not valid, rule vt.max_size check failed")
}
}
return nil
}
func (p *ValidationFunctionTest) Validate() error {
if p.StringLength != int64(len(p.StringFoo)){
return thrift.NewTApplicationException(thrift.VALIDATOR_CHECK_FAILED, "ValidationFunctionTest.StringLength not valid, rule vt.in check failed")
}
return nil
}
@GuangmingLuo none of the travis test passed, it doesn't look the compiler code compiles, so maybe fix that first?
also in my previous comment I said that:
it would also be helpful (since the review on the spec/proposal part is mostly done) to paste the compiler generated go code into the pr so it's easier to review the code.
and I don't see anyone pasting any generated validator go code into the comments.
@fishy Sorry for missing attaching the generated code example. I have pasted the ValidateTest.thrift and related generated code above. And I have fixed the cmake compile error and rebased the master branch, please take another look.
https://app.travis-ci.com/github/apache/thrift/jobs/578599367 is a new failure introduced by this PR
Feedback on the compiler generated go code:
- I thought we agreed on https://github.com/apache/thrift/pull/2469#discussion_r858950006 to use
TApplicationExceptionfor validation errors, but the example is still usingerrors.New - In error messages the
p.prefix doesn't really bring any extra value, it would be more useful to use the type name instead ofp(example: "BasicTest.Byte0 not valid, ...") - (minor) For
vt.incheck you can break the for loop early after first find - (minor) For
vt.patterncheckregexp.MatchStringcalls would be a performance nightmare because it needs to compile the pattern on every check. It's probably OK for the first version but we should consider declare global variables to compile the patterns defined in thrift files. - Is
IsValidcalled byRead/Writeor does it need to be called explicitly (e.g. manual check only)?
Feedback on the compiler generated go code:
- I thought we agreed on THRIFT-5423: Support go parameter validation in IDL #2469 (comment) to use
TApplicationExceptionfor validation errors, but the example is still usingerrors.New- In error messages the
p.prefix doesn't really bring any extra value, it would be more useful to use the type name instead ofp(example: "BasicTest.Byte0 not valid, ...")- (minor) For
vt.incheck you can break the for loop early after first find- (minor) For
vt.patterncheckregexp.MatchStringcalls would be a performance nightmare because it needs to compile the pattern on every check. It's probably OK for the first version but we should consider declare global variables to compile the patterns defined in thrift files.- Is
IsValidcalled byRead/Writeor does it need to be called explicitly (e.g. manual check only)?
Thanks for your quick reply. I have fixed the runtime error that leads to travis ci fail.
I have added a new exception type VALIDATOR_CHECK_FAILED to application exceptions, and use it as an exception when a validation rule check fails.
For 2 && 3, thanks for your suggestion, now we will use {struct name}.{field name} in validator exception and validator will break early in vt.in.
About 4, I will try to optimize this part, or we can do this in the later version.
About 5, in my view it should be called explicitly. If it is called by Read/Write, it could cause serialization /deserialization to fail.
it could cause serialization /deserialization to fail.
I thought that's the point :)
But I think it's fine to make it manual checks only first then iterate on top of that later. But I think the function name IsValid implies that it would return a bool but it actually returns an error. Validate() error is probably a better name.
Validate() erroris probably a better name.
Sounds a better practice, fix done.
@fishy There is a concurrency_test error in AppVeyor, but I can't reproduce it locally. Is that an occasional case or does it fails due to my changes? And all fixes mentioned above have been done, please take another look
The code is generally OK, minor issues aside.
We are adding a new
VALIDATION_FAILEDtoTApplicationException
This should be fine, I can't think of a realistic scenario where it'd be a problem.
More generally, I'm not sure about "tool functions". It can easily cause further drift between supported languages. I think we should define and document which functions we support (len() is fine). We can add more in future versions if necessary.
The code is generally OK, minor issues aside.
We are adding a new
VALIDATION_FAILEDtoTApplicationExceptionThis should be fine, I can't think of a realistic scenario where it'd be a problem.
More generally, I'm not sure about "tool functions". It can easily cause further drift between supported languages. I think we should define and document which functions we support (
len()is fine). We can add more in future versions if necessary.
I add a supported functions table to the validator proposal, which can be modified when more functions or language supports are available.
I add a supported functions table to the validator proposal, which can be modified when more functions or language supports are available.
Looks good. Before we merge, let's move the doc from doc/proposal to docs/specs. You should also add a line to CHANGES.md for 0.17.
I add a supported functions table to the validator proposal, which can be modified when more functions or language supports are available.
Looks good. Before we merge, let's move the doc from
doc/proposaltodocs/specs. You should also add a line toCHANGES.mdfor 0.17.
I also rebased the master branch, if commits need to be squashed please let me know.
Yes, please squash them. Commit message can be THRIFT-5423: IDL parameter validation for Go.
Another suggestion, which can be done in a follow-up PR instead, but doing it in this PR will help a lot of the test code:
Define a ValidationError type in go library, and implement Unwrap on tApplicationException so that when a TApplicationException is validation error it can also unwrap to that, that way when checking for ValidationError you can use its fields to determine what's the check (e.g. vt.const) and what's the field that failed validation.
Another suggestion, which can be done in a follow-up PR instead, but doing it in this PR will help a lot of the test code:
Define a
ValidationErrortype in go library, and implementUnwrapontApplicationExceptionso that when aTApplicationExceptionis validation error it can also unwrap to that, that way when checking forValidationErroryou can use its fields to determine what's the check (e.g.vt.const) and what's the field that failed validation.
sorry for the late reply, I have defined a ValidationError in application_exception.go and add a NewValidationException which is used to construct tApplicationExcetion with ValidationError. I also add a unwrap test in the validation test to make sure Unwrap works correctly.
@fishy All fixes done, PTAL