diff --git a/glide.lock b/glide.lock index 2cb724cf3a..df4a941c3d 100644 --- a/glide.lock +++ b/glide.lock @@ -1,10 +1,12 @@ -hash: 36c2204f43f77bbff3766ab60737169c9239f2ea903c2fd7e85f4309323cc820 -updated: 2016-08-02T14:48:37.043081154+02:00 +hash: 62c37b7f2fdfa1d53eec7f5c805018cf237bf16bd721487664f8ee13de505cf7 +updated: 2016-08-12T15:27:52.439580409+05:30 imports: - name: bitbucket.org/pkg/inflect version: 8961c3750a47b8c0b3e118d52513b97adf85a7e8 - name: github.com/armon/go-metrics version: fbf75676ee9c0a3a23eb0a4d9220a3612cfbd1ed +- name: github.com/asaskevich/govalidator + version: 593d64559f7600f29581a3ee42177f5dbded27a9 - name: github.com/axw/gocov version: 54b98cfcac0c63fb3f9bd8e7ad241b724d4e985b subpackages: @@ -87,6 +89,8 @@ imports: version: 527b253f588776e5f72a0a0d1e93195cd3f82707 subpackages: - go/ast/astutil +- name: gopkg.in/asaskevich/govalidator.v4 + version: 7664702784775e51966f0885f5cd27435916517b - name: gopkg.in/yaml.v2 version: a83829b6f1293c91addabc89d0571c246397bbf4 testImports: [] diff --git a/glide.yaml b/glide.yaml index b39ef18aff..4873510ecd 100644 --- a/glide.yaml +++ b/glide.yaml @@ -51,3 +51,4 @@ import: subpackages: - gocov - package: github.com/wadey/gocovmerge +- package: gopkg.in/asaskevich/govalidator.v4 diff --git a/models/fieldtype_test.go b/models/fieldtype_test.go new file mode 100644 index 0000000000..a349fd8260 --- /dev/null +++ b/models/fieldtype_test.go @@ -0,0 +1,134 @@ +package models + +import "testing" + +var ( + stString = SimpleType{KindString} + stInt = SimpleType{KindInteger} + stFloat = SimpleType{KindFloat} + stDuration = SimpleType{KindDuration} + stURL = SimpleType{KindURL} + stList = SimpleType{KindList} +) + +type input struct { + t FieldType + value interface{} + expectedValue interface{} + errorExpected bool +} + +func TestSimpleTypeConversion(t *testing.T) { + test_data := []input{ + {stString, "hello world", "hello world", false}, + {stString, "", "", false}, + {stString, 100, nil, true}, + {stString, 1.90, nil, true}, + + {stInt, 100.0, nil, true}, + {stInt, 100, 100, false}, + {stInt, "100", nil, true}, + {stInt, true, nil, true}, + + {stFloat, 1.1, 1.1, false}, + {stFloat, 1, nil, true}, + {stFloat, "a", nil, true}, + + {stDuration, 0, 0, false}, + {stDuration, 1.1, nil, true}, + {stDuration, "duration", nil, true}, + + {stURL, "http://www.google.com", "http://www.google.com", false}, + {stURL, "", nil, true}, + {stURL, "google", nil, true}, + {stURL, "http://google.com", "http://google.com", false}, + + {stList, [4]int{1, 2, 3, 4}, [4]int{1, 2, 3, 4}, false}, + {stList, [2]string{"1", "2"}, [2]string{"1", "2"}, false}, + {stList, "", nil, true}, + // {stList, []int{}, []int{}, false}, need to find out the way for empty array. + // because slices do not have equality operator. + } + for _, inp := range test_data { + retVal, err := inp.t.ConvertToModel(inp.value) + if retVal == inp.expectedValue && (err != nil) == inp.errorExpected { + t.Log("test pass for input: ", inp) + } else { + t.Error(retVal, err) + t.Fail() + } + } +} + +var ( + stEnum = SimpleType{KindEnum} + enum = EnumType{ + BaseType: stEnum, + // ENUM with same type values + Values: []interface{}{"new", "triaged", "WIP", "QA", "done"}, + } + + multipleTypeEnum = EnumType{ + BaseType: stEnum, + // ENUM with different type values. + Values: []interface{}{100, 1.1, "hello"}, + } +) + +func TestEnumTypeConversion(t *testing.T) { + data := []input{ + {enum, "string", nil, true}, + {enum, "triaged", "triaged", false}, + {enum, "done", "done", false}, + {enum, "", nil, true}, + {enum, 100, nil, true}, + + {multipleTypeEnum, "abcd", nil, true}, + {multipleTypeEnum, 100, 100, false}, + {multipleTypeEnum, "hello", "hello", false}, + } + for _, inp := range data { + retVal, err := inp.t.ConvertToModel(inp.value) + if retVal == inp.expectedValue && (err != nil) == inp.errorExpected { + t.Log("test pass for input: ", inp) + } else { + t.Error(retVal, err) + t.Fail() + } + } +} + +var ( + intList = ListType{ + SimpleType: SimpleType{KindList}, + ComponentType: SimpleType{KindInteger}, + } + strList = ListType{ + SimpleType: SimpleType{KindList}, + ComponentType: SimpleType{KindString}, + } +) + +func TestListTypeConversion(t *testing.T) { + data := []input{ + {intList, [2]int{11, 2}, "array/slice", false}, + {intList, [2]string{"11", "2"}, nil, true}, + + {strList, [2]string{"11", "2"}, "array/slice", false}, + {strList, [2]int{11, 2}, nil, true}, + } + + for _, inp := range data { + // Ignore expectedValue for now. + // slices can be compared only with nil. + // Because we will need to iterate and match the output. + retVal, err := inp.t.ConvertToModel(inp.value) + if (err != nil) == inp.errorExpected { + t.Log("test pass for input: ", inp) + } else { + t.Error("failed for input=", inp) + t.Error(retVal, err) + t.Fail() + } + } +} diff --git a/models/list_type.go b/models/list_type.go index 5f8729ea2e..13ce404460 100644 --- a/models/list_type.go +++ b/models/list_type.go @@ -34,14 +34,15 @@ func convertList(converter converter, fieldType ListType, value interface{}) (in // the assumption is that work item types do not change over time...only new ones can be created valueType := reflect.TypeOf(value) - if valueType.Kind() != reflect.Array { - return nil, fmt.Errorf("value %v should be %s, but is %s", value, "array", valueType.Name()) + if (valueType.Kind() != reflect.Array) && (valueType.Kind() != reflect.Slice) { + return nil, fmt.Errorf("value %v should be %s, but is %s", value, "array/slice", valueType.Name()) } - valueArray := value.([]interface{}) - converted := make([]interface{}, len(valueArray)) + valueArray := reflect.ValueOf(value) + converted := make([]interface{}, valueArray.Len()) for i := range converted { var err error - converted[i], err = converter(fieldType, valueArray[i]) + // valueArray index value must be converted to Interface else it has TYPE=Value + converted[i], err = converter(fieldType.ComponentType, valueArray.Index(i).Interface()) if err != nil { return nil, fmt.Errorf("error converting list value: %s", err.Error()) } diff --git a/models/simple_type.go b/models/simple_type.go index a201fda903..39983bbdb6 100644 --- a/models/simple_type.go +++ b/models/simple_type.go @@ -5,6 +5,8 @@ import ( "reflect" "strconv" "time" + + "github.com/asaskevich/govalidator" ) // SimpleType is an unstructured FieldType @@ -23,18 +25,28 @@ var timeType = reflect.TypeOf((*time.Time)(nil)).Elem() func (fieldType SimpleType) ConvertToModel(value interface{}) (interface{}, error) { valueType := reflect.TypeOf(value) switch fieldType.GetKind() { - case KindString, KindURL, KindUser: + case KindString, KindUser: if valueType.Kind() != reflect.String { return nil, fmt.Errorf("value %v should be %s, but is %s", value, "string", valueType.Name()) } return value, nil - case KindInteger, KindFloat, KindDuration: - // instant == milliseconds + case KindURL: + if valueType.Kind() == reflect.String && govalidator.IsURL(value.(string)) { + return value, nil + } + return nil, fmt.Errorf("value %v should be %s, but is %s", value, "URL", valueType.Name()) + case KindFloat: if valueType.Kind() != reflect.Float64 { return nil, fmt.Errorf("value %v should be %s, but is %s", value, "float64", valueType.Name()) } return value, nil + case KindInteger, KindDuration: + if valueType.Kind() != reflect.Int { + return nil, fmt.Errorf("value %v should be %s, but is %s", value, "int", valueType.Name()) + } + return value, nil case KindInstant: + // instant == milliseconds if !valueType.Implements(timeType) { return nil, fmt.Errorf("value %v should be %s, but is %s", value, "time.Time", valueType.Name()) } @@ -45,7 +57,14 @@ func (fieldType SimpleType) ConvertToModel(value interface{}) (interface{}, erro } idValue, err := strconv.Atoi(value.(string)) return idValue, err - + case KindList: + if (valueType.Kind() != reflect.Array) && (valueType.Kind() != reflect.Slice) { + return nil, fmt.Errorf("value %v should be %s, but is %s,", value, "array/slice", valueType.Kind()) + } + return value, nil + case KindEnum: + // to be done yet | not sure what to write here as of now. + return value, nil default: return nil, fmt.Errorf("unexpected type constant: %d", fieldType.GetKind()) }