Skip to content

Commit

Permalink
list failed checks against a single parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
tmus committed Aug 6, 2019
1 parent 1c3c785 commit 7ae6eef
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 43 deletions.
9 changes: 2 additions & 7 deletions message.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
package validate

// Message represents a failed validation. It contains details
// of the param that failed, as well as the error message from
// the rule that caused it to fail.
type Message struct {
Error string `json:"error"`
Param string `json:"param"`
}
// Message represents a failed validation.
type Message map[string][]string
2 changes: 1 addition & 1 deletion rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func TestRules(t *testing.T) {
r.Form.Set("parameter", value)
msgs, _ := Check(r, Rule{"parameter", rule.Check, rule.Options})
if len(msgs) > 0 {
fmt.Println("Got an error, expected none:", msgs[0].Error)
fmt.Println("Got an error, expected none:", msgs["parameter"])
fmt.Println("Value was", value)
t.FailNow()
}
Expand Down
19 changes: 7 additions & 12 deletions validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ type Validator struct {
// Respond is a helper method that writes the errors to the given
// http.ResponseWriter. This also sets an appropriate HTTP header
// and sets the content-type to JSON.
func Respond(w http.ResponseWriter, m []Message) {
func Respond(w http.ResponseWriter, m Message) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnprocessableEntity)

eb := make(map[string][]Message)
eb := make(map[string]map[string][]string)
eb["errors"] = m

d, _ := json.Marshal(eb)
Expand All @@ -33,7 +33,7 @@ func Respond(w http.ResponseWriter, m []Message) {
// Check is an all-in-one method of creating and running a new
// Validator. If there is no logic around adding rules, it is
// the easiest way to run a Validator.
func Check(r *http.Request, rule ...Rule) ([]Message, error) {
func Check(r *http.Request, rule ...Rule) (Message, error) {
return Make(r, rule...).Run()
}

Expand All @@ -49,21 +49,16 @@ func Make(r *http.Request, rule ...Rule) *Validator {

// Run determines if the given rules are satisfied by the request.
// A "perfect" outcome is `nil, nil`.
func (v *Validator) Run() ([]Message, error) {
func (v *Validator) Run() (Message, error) {
if len(v.Rules) == 0 {
return nil, fmt.Errorf("no rules defined on validator")
}

// The number of messages can't exceed the number of rules,
// so define an upper limit here for speed.
vm := make([]Message, 0, len(v.Rules))
vm := make(Message)

for _, rule := range v.Rules {
if err := rule.Check(v.request, rule.Param, rule.Options); err != nil {
vm = append(vm, Message{
Error: err.Error(),
Param: rule.Param,
})
vm[rule.Param] = append(vm[rule.Param], err.Error())
}
}

Expand All @@ -83,6 +78,6 @@ type Bag string

const ErrorBag Bag = "errorbag"

func ErrorContext(r *http.Request, msgs []Message) context.Context {
func ErrorContext(r *http.Request, msgs Message) context.Context {
return context.WithValue(r.Context(), ErrorBag, msgs)
}
62 changes: 39 additions & 23 deletions validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,6 @@ import (
"testing"
)

func TestAddErrorsToContext(t *testing.T) {
r, _ := http.NewRequest("GET", "localhost", nil)

rule := Rule{
Param: "forename",
Check: func(r *http.Request, param string, _ Options) error {
return errors.New("fail")
},
}

msgs, _ := Check(r, rule)
r = r.WithContext(ErrorContext(r, msgs))

val := r.Context().Value(ErrorBag)
_ = val.([]Message)

// TODO: Test this
}

func TestCheckCreatesValidatorAndRunsIt(t *testing.T) {
r, _ := http.NewRequest("GET", "localhost", nil)

Expand Down Expand Up @@ -133,7 +114,8 @@ func TestValidatorReturnsRuleCheckErrorMessage(t *testing.T) {
validator.Add(rule)

messages, _ := validator.Run()
if messages[0].Error != "forced failure" {

if messages["forename"][0] != "forced failure" {
fmt.Println("expected the message to contain the rule error. It didn't.")
t.FailNow()
}
Expand All @@ -154,8 +136,42 @@ func TestValidatorReturnsParamInError(t *testing.T) {
validator.Add(rule)

messages, _ := validator.Run()
if messages[0].Param != "forename" {
fmt.Println("expected the message to contain the param. It didn't.")
t.FailNow()

for param := range messages {
if param != "forename" {
fmt.Println("expected the message to contain the param. It didn't.")
t.FailNow()
}
}
}

func TestParamHasNestedErrors(t *testing.T) {
r, _ := http.NewRequest("GET", "localhost", nil)

validator := Make(r)

rule := Rule{
Param: "forename",
Check: func(r *http.Request, param string, _ Options) error {
return errors.New("forced failure")
},
}

rule2 := Rule{
Param: "forename",
Check: func(r *http.Request, param string, _ Options) error {
return errors.New("forced failure")
},
}

validator.Add(rule, rule2)

messages, _ := validator.Run()

for _, msgs := range messages {
if len(msgs) != 2 {
fmt.Println("expected the param to contain two errors. It didn't")
t.FailNow()
}
}
}

0 comments on commit 7ae6eef

Please sign in to comment.