Skip to content

Commit

Permalink
feat: return multiple errors on validation call
Browse files Browse the repository at this point in the history
Merge pull request #22 from qri-io/err_struct
  • Loading branch information
b5 authored Jan 22, 2018
2 parents b6ed67c + 2874aff commit 5689bf7
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 122 deletions.
22 changes: 13 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,16 @@ func main() {
"lastName" : "Michael"
}`)

if err := rs.ValidateBytes(valid); err != nil {
if errors := rs.ValidateBytes(valid); len(errors) > 0 {
panic(err)
}

var invalidPerson = []byte(`{
"firstName" : "Prince"
}`)
err := rs.ValidateBytes(invalidPerson)
fmt.Println(err.Error())
if errors := rs.ValidateBytes(invalidPerson); len(errors) > 0 {
fmt.Println(errs[0].Error())
}

var invalidFriend = []byte(`{
"firstName" : "Jay",
Expand All @@ -92,8 +93,9 @@ func main() {
"firstName" : "Nas"
}]
}`)
err = rs.ValidateBytes(invalidFriend)
fmt.Println(err)
if errors := rs.ValidateBytes(invalidFriend); len(errors) > 0 {
fmt.Println(errors[0].Error())
}
}
```

Expand Down Expand Up @@ -124,10 +126,12 @@ func newIsFoo() jsonschema.Validator {
}

// Validate implements jsonschema.Validator
func (f IsFoo) Validate(data interface{}) error {
func (f IsFoo) Validate(data interface{}) []jsonschema.ValError {
if str, ok := data.(string); ok {
if str != "foo" {
return fmt.Errorf("'%s' is not foo. It should be foo. plz make '%s' == foo. plz", str, str)
return []jsonschema.ValError{
{Message: fmt.Sprintf("'%s' is not foo. It should be foo. plz make '%s' == foo. plz", str, str)},
}
}
}
return nil
Expand All @@ -148,10 +152,10 @@ func main() {
}

// validate some JSON
err := rs.ValidateBytes([]byte(`"bar"`))
errors := rs.ValidateBytes([]byte(`"bar"`))

// print le error
fmt.Println(err.Error())
fmt.Println(errs[0].Error())

// Output: 'bar' is not foo. It should be foo. plz make 'bar' == foo. plz
}
Expand Down
28 changes: 20 additions & 8 deletions keywords.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,28 @@ func newTipe() Validator {
}

// Validate checks to see if input data satisfies the type constraint
func (t tipe) Validate(data interface{}) error {
func (t tipe) Validate(data interface{}) (errs []ValError) {
jt := DataType(data)
for _, typestr := range t.vals {
if jt == typestr || jt == "integer" && typestr == "number" {
return nil
}
}
if len(t.vals) == 1 {
return fmt.Errorf(`expected "%v" to be of type %s`, data, t.vals[0])
errs = append(errs, ValError{
Message: fmt.Sprintf(`expected "%v" to be of type %s`, data, t.vals[0]),
})
return
}

str := ""
for _, ts := range t.vals {
str += ts + ","
}
return fmt.Errorf(`expected "%v" to be one of type: %s`, data, str[:len(str)-1])
errs = append(errs, ValError{
Message: fmt.Sprintf(`expected "%v" to be one of type: %s`, data, str[:len(str)-1]),
})
return
}

// JSONProp implements JSON property name indexing for tipe
Expand Down Expand Up @@ -138,13 +144,15 @@ func (e enum) String() string {
}

// Validate implements the Validator interface for enum
func (e enum) Validate(data interface{}) error {
func (e enum) Validate(data interface{}) []ValError {
for _, v := range e {
if err := v.Validate(data); err == nil {
return nil
}
}
return fmt.Errorf("expected %s to be one of %s", data)
return []ValError{
{Message: fmt.Sprintf("expected %s to be one of %s", data)},
}
}

// JSONProp implements JSON property name indexing for enum
Expand Down Expand Up @@ -178,14 +186,18 @@ func newKonst() Validator {
}

// Validate implements the validate interface for konst
func (c konst) Validate(data interface{}) error {
func (c konst) Validate(data interface{}) []ValError {
var con interface{}
if err := json.Unmarshal(c, &con); err != nil {
return err
return []ValError{
{Message: err.Error()},
}
}

if !reflect.DeepEqual(con, data) {
return fmt.Errorf(`%s must equal %s`, string(c), data)
return []ValError{
{Message: fmt.Sprintf(`%s must equal %s`, string(c), data)},
}
}
return nil
}
Expand Down
44 changes: 26 additions & 18 deletions keywords_arrays.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,19 @@ func newItems() Validator {
}

// Validate implements the Validator interface for items
func (it items) Validate(data interface{}) error {
func (it items) Validate(data interface{}) []ValError {
if arr, ok := data.([]interface{}); ok {
if it.single {
for i, elem := range arr {
if err := it.Schemas[0].Validate(elem); err != nil {
return fmt.Errorf("element %d %s", i, err.Error())
for _, elem := range arr {
if ves := it.Schemas[0].Validate(elem); len(ves) > 0 {
return ves
}
}
} else {
for i, vs := range it.Schemas {
if i < len(arr) {
if err := vs.Validate(arr[i]); err != nil {
return fmt.Errorf("element %d %s", i, err.Error())
if ves := vs.Validate(arr[i]); len(ves) > 0 {
return ves
}
}
}
Expand Down Expand Up @@ -108,20 +108,20 @@ func newAdditionalItems() Validator {
}

// Validate implements the Validator interface for additionalItems
func (a *additionalItems) Validate(data interface{}) error {
func (a *additionalItems) Validate(data interface{}) (errs []ValError) {
if a.startIndex >= 0 {
if arr, ok := data.([]interface{}); ok {
for i, elem := range arr {
if i < a.startIndex {
continue
}
if err := a.Schema.Validate(elem); err != nil {
return fmt.Errorf("element %d: %s", i, err.Error())
if ves := a.Schema.Validate(elem); len(ves) > 0 {
errs = append(errs, ves...)
}
}
}
}
return nil
return
}

// JSONProp implements JSON property name indexing for additionalItems
Expand Down Expand Up @@ -158,10 +158,12 @@ func newMaxItems() Validator {
}

// Validate implements the Validator interface for maxItems
func (m maxItems) Validate(data interface{}) error {
func (m maxItems) Validate(data interface{}) []ValError {
if arr, ok := data.([]interface{}); ok {
if len(arr) > int(m) {
return fmt.Errorf("%d array items exceeds %d max", len(arr), m)
return []ValError{
{Message: fmt.Sprintf("%d array items exceeds %d max", len(arr), m)},
}
}
}
return nil
Expand All @@ -177,10 +179,12 @@ func newMinItems() Validator {
}

// Validate implements the Validator interface for minItems
func (m minItems) Validate(data interface{}) error {
func (m minItems) Validate(data interface{}) []ValError {
if arr, ok := data.([]interface{}); ok {
if len(arr) < int(m) {
return fmt.Errorf("%d array items below %d minimum", len(arr), m)
return []ValError{
{Message: fmt.Sprintf("%d array items below %d minimum", len(arr), m)},
}
}
}
return nil
Expand All @@ -197,13 +201,15 @@ func newUniqueItems() Validator {
}

// Validate implements the Validator interface for uniqueItems
func (u *uniqueItems) Validate(data interface{}) error {
func (u *uniqueItems) Validate(data interface{}) []ValError {
if arr, ok := data.([]interface{}); ok {
found := []interface{}{}
for _, elem := range arr {
for _, f := range found {
if reflect.DeepEqual(f, elem) {
return fmt.Errorf("arry must be unique: %v", arr)
return []ValError{
{Message: fmt.Sprintf("arry must be unique: %v", arr)},
}
}
}
found = append(found, elem)
Expand All @@ -221,15 +227,17 @@ func newContains() Validator {
}

// Validate implements the Validator interface for contains
func (c *contains) Validate(data interface{}) error {
func (c *contains) Validate(data interface{}) []ValError {
v := Schema(*c)
if arr, ok := data.([]interface{}); ok {
for _, elem := range arr {
if err := v.Validate(elem); err == nil {
return nil
}
}
return fmt.Errorf("expected %v to contain at least one of: %s", data, c)
return []ValError{
{Message: fmt.Sprintf("expected %v to contain at least one of: %s", data, c)},
}
}
return nil
}
Expand Down
32 changes: 20 additions & 12 deletions keywords_booleans.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ func newAllOf() Validator {
}

// Validate implements the validator interface for allOf
func (a allOf) Validate(data interface{}) error {
for i, sch := range a {
if err := sch.Validate(data); err != nil {
return fmt.Errorf("allOf element %d error: %s", i, err.Error())
func (a allOf) Validate(data interface{}) (errs []ValError) {
for _, sch := range a {
if ves := sch.Validate(data); len(ves) > 0 {
errs = append(errs, ves...)
}
}
return nil
return
}

// JSONProp implements JSON property name indexing for allOf
Expand Down Expand Up @@ -55,13 +55,15 @@ func newAnyOf() Validator {
}

// Validate implements the validator interface for anyOf
func (a anyOf) Validate(data interface{}) error {
func (a anyOf) Validate(data interface{}) []ValError {
for _, sch := range a {
if err := sch.Validate(data); err == nil {
return nil
}
}
return fmt.Errorf("value did not match any specified anyOf schemas: %v", data)
return []ValError{
{Message: fmt.Sprintf("value did not match any specified anyOf schemas: %v", data)},
}
}

// JSONProp implements JSON property name indexing for anyOf
Expand Down Expand Up @@ -94,18 +96,22 @@ func newOneOf() Validator {
}

// Validate implements the validator interface for oneOf
func (o oneOf) Validate(data interface{}) error {
func (o oneOf) Validate(data interface{}) []ValError {
matched := false
for _, sch := range o {
if err := sch.Validate(data); err == nil {
if matched {
return fmt.Errorf("value matched more than one specified oneOf schemas")
return []ValError{
{Message: fmt.Sprintf("value matched more than one specified oneOf schemas")},
}
}
matched = true
}
}
if !matched {
return fmt.Errorf("value did not match any of the specified oneOf schemas")
return []ValError{
{Message: fmt.Sprintf("value did not match any of the specified oneOf schemas")},
}
}
return nil
}
Expand Down Expand Up @@ -141,11 +147,13 @@ func newNot() Validator {
}

// Validate implements the validator interface for not
func (n *not) Validate(data interface{}) error {
func (n *not) Validate(data interface{}) []ValError {
sch := Schema(*n)
if sch.Validate(data) == nil {
// TODO - make this error actually make sense
return fmt.Errorf("not clause")
return []ValError{
{Message: fmt.Sprintf("not clause")},
}
}
return nil
}
Expand Down
6 changes: 3 additions & 3 deletions keywords_conditionals.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func newIif() Validator {
}

// Validate implements the Validator interface for iif
func (i *iif) Validate(data interface{}) error {
func (i *iif) Validate(data interface{}) []ValError {
if err := i.Schema.Validate(data); err == nil {
if i.then != nil {
s := Schema(*i.then)
Expand Down Expand Up @@ -71,7 +71,7 @@ func newThen() Validator {
}

// Validate implements the Validator interface for then
func (t *then) Validate(data interface{}) error {
func (t *then) Validate(data interface{}) []ValError {
return nil
}

Expand Down Expand Up @@ -110,7 +110,7 @@ func newEls() Validator {
}

// Validate implements the Validator interface for els
func (e *els) Validate(data interface{}) error {
func (e *els) Validate(data interface{}) []ValError {
return nil
}

Expand Down
Loading

0 comments on commit 5689bf7

Please sign in to comment.