Skip to content

Commit

Permalink
Merge pull request #24 from cohesivestack/feature/ensure-consistency-…
Browse files Browse the repository at this point in the history
…in-docs-examples

Feature/ensure consistency in docs examples
  • Loading branch information
carlosforero authored Apr 8, 2024
2 parents b345f32 + 2ac9994 commit 241f75a
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 13 deletions.
5 changes: 3 additions & 2 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,10 @@ func (e *Error) Errors() map[string]*valueError {
return e.errors
}

// Return the JSON encoding of the validation error messages.
// Returns the JSON encoding of the validation error messages.
//
// A custom function can be set with [SetMarshalJson()]
// A custom function can be set either by passing it as a parameter to
// [validation.Error()] or through [FactoryOptions].
func (e *Error) MarshalJSON() ([]byte, error) {
if e.marshalJsonFunc != nil {
return e.marshalJsonFunc(e)
Expand Down
36 changes: 36 additions & 0 deletions error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,39 @@ func TestCustomErrorMarshallJSON(t *testing.T) {
assert.Contains(t, emailErrors, "Email can't be blank")
assert.Contains(t, emailErrors, "Email must match to \"a\"")
}

func TestCustomErrorMarshallJSONParameter(t *testing.T) {

customFunc := func(e *Error) ([]byte, error) {

errors := map[string]interface{}{}

for k, v := range e.errors {
if len(v.Messages()) == 1 {
errors[k] = v.Messages()[0]
} else {
errors[k] = v.Messages()
}
}

// Add root level errors to customize errors interface
return json.Marshal(map[string]map[string]interface{}{"errors": errors})
}

r, _ := regexp.Compile("a")
v := Check(
String("", "email").Not().Blank().MatchingTo(r),
String("", "name").Not().Blank())

jsonByte, err := json.Marshal(v.Error(customFunc))
assert.NoError(t, err)

jsonMap := map[string]map[string]interface{}{}
err = json.Unmarshal(jsonByte, &jsonMap)
assert.NoError(t, err)

assert.Equal(t, "Name can't be blank", jsonMap["errors"]["name"])
emailErrors := jsonMap["errors"]["email"].([]interface{})
assert.Contains(t, emailErrors, "Email can't be blank")
assert.Contains(t, emailErrors, "Email must match to \"a\"")
}
25 changes: 25 additions & 0 deletions util.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package valgo

import (
"encoding/json"
"sort"
"strings"
"unicode"
)
Expand Down Expand Up @@ -65,3 +67,26 @@ func humanizeName(name string) string {

return out.String()
}

// serializes the error messages into sorted JSON for consistency in
// documentation examples.
func sortedErrorMarshalForDocs(e *Error) ([]byte, error) {
// Create a slice to hold the keys from the map, so we can sort them.
keys := make([]string, 0, len(e.errors))
for k := range e.errors {
keys = append(keys, k)
}
sort.Strings(keys) // Sort the keys alphabetically.

// Create a map to hold the sorted key-value pairs.
sortedErrors := make(map[string]interface{}, len(keys))
for _, k := range keys {
messages := make([]string, len(e.errors[k].Messages()))
copy(messages, e.errors[k].Messages())
sort.Strings(messages)
sortedErrors[k] = messages
}

// Marshal the sorted map to JSON.
return json.Marshal(sortedErrors)
}
20 changes: 15 additions & 5 deletions valgo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ func Example() {
Is(Number(17, "age").GreaterThan(18))

if !val.Valid() {
out, _ := json.MarshalIndent(val.Error(), "", " ")
// NOTE: sortedErrorMarshalForDocs is an optional parameter used here for
// documentation purposes to ensure the order of keys in the JSON output.
out, _ := json.MarshalIndent(val.Error(sortedErrorMarshalForDocs), "", " ")
fmt.Println(string(out))
}
// Output: {
Expand All @@ -30,7 +32,9 @@ func ExampleIs() {
Is(String("singl", "status").InSlice([]string{"married", "single"}))

if !val.Valid() {
out, _ := json.MarshalIndent(val.Error(), "", " ")
// NOTE: sortedErrorMarshalForDocs is an optional parameter used here for
// documentation purposes to ensure the order of keys in the JSON output.
out, _ := json.MarshalIndent(val.Error(sortedErrorMarshalForDocs), "", " ")
fmt.Println(string(out))
}

Expand Down Expand Up @@ -85,7 +89,9 @@ func ExampleIn() {
String(p.Address.Street, "street").Not().Blank()))

if !val.Valid() {
out, _ := json.MarshalIndent(val.Error(), "", " ")
// NOTE: sortedErrorMarshalForDocs is an optional parameter used here for
// documentation purposes to ensure the order of keys in the JSON output.
out, _ := json.MarshalIndent(val.Error(sortedErrorMarshalForDocs), "", " ")
fmt.Println(string(out))
}

Expand Down Expand Up @@ -127,7 +133,9 @@ func ExampleInRow() {
}

if !val.Valid() {
out, _ := json.MarshalIndent(val.Error(), "", " ")
// NOTE: sortedErrorMarshalForDocs is an optional parameter used here for
// documentation purposes to ensure the order of keys in the JSON output.
out, _ := json.MarshalIndent(val.Error(sortedErrorMarshalForDocs), "", " ")
fmt.Println(string(out))
}

Expand All @@ -148,7 +156,9 @@ func ExampleCheck() {
val := Check(String("", "full_name").Not().Blank().OfLengthBetween(4, 20))

if !val.Valid() {
out, _ := json.MarshalIndent(val.Error(), "", " ")
// NOTE: sortedErrorMarshalForDocs is an optional parameter used here for
// documentation purposes to ensure the order of keys in the JSON output.
out, _ := json.MarshalIndent(val.Error(sortedErrorMarshalForDocs), "", " ")
fmt.Println(string(out))
}

Expand Down
20 changes: 14 additions & 6 deletions validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,19 +228,27 @@ func (validation *Validation) invalidate(name *string, title *string, fragment *
et.params = fragment.templateParams
}

// Return a map with the information for each invalid field validator in the
// [Validation] session.
// Return a map with the information for each invalid field validator
// in the Validation session.
func (session *Validation) Errors() map[string]*valueError {
return session.errors
}

// Return a map with the information for each invalid field validator in the
// [Validation] session.
func (validation *Validation) Error() error {
// Return an error object that encapsulates the validation errors created during
// the Validation session. If the session is valid, it returns nil.
//
// An optional JSON marshaling function can be passed to customize how the
// validation errors are serialized into JSON. If no function is provided, a
// default marshaling behavior is used.
func (validation *Validation) Error(marshalJsonFun ...func(e *Error) ([]byte, error)) error {
if !validation.valid {
fn := validation.marshalJsonFunc
if len(marshalJsonFun) > 0 {
fn = marshalJsonFun[0]
}
return &Error{
errors: validation.errors,
marshalJsonFunc: validation.marshalJsonFunc,
marshalJsonFunc: fn,
}
}
return nil
Expand Down

0 comments on commit 241f75a

Please sign in to comment.