X-Git-Url: https://gerrit.akraino.org/r/gitweb?a=blobdiff_plain;f=src%2Ffoundation%2Fapi%2Frevel%2Fvalidation.go;fp=src%2Ffoundation%2Fapi%2Frevel%2Fvalidation.go;h=49cf3a9dcd9430060d861828a664a31d809203c3;hb=1d1ee6961c93781e1187d8c7faa868da6b2f01f4;hp=0000000000000000000000000000000000000000;hpb=56dd5e0f2164b37b40ac1daa188ccc618b4cbd19;p=iec.git diff --git a/src/foundation/api/revel/validation.go b/src/foundation/api/revel/validation.go new file mode 100644 index 0000000..49cf3a9 --- /dev/null +++ b/src/foundation/api/revel/validation.go @@ -0,0 +1,334 @@ +// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved. +// Revel Framework source code and usage is governed by a MIT style +// license that can be found in the LICENSE file. + +package revel + +import ( + "fmt" + "net/http" + "net/url" + "regexp" + "runtime" +) + +// ValidationError simple struct to store the Message & Key of a validation error +type ValidationError struct { + Message, Key string +} + +// String returns the Message field of the ValidationError struct. +func (e *ValidationError) String() string { + if e == nil { + return "" + } + return e.Message +} + +// Validation context manages data validation and error messages. +type Validation struct { + Errors []*ValidationError + Request *Request + Translator func(locale, message string, args ...interface{}) string + keep bool +} + +// Keep tells revel to set a flash cookie on the client to make the validation +// errors available for the next request. +// This is helpful when redirecting the client after the validation failed. +// It is good practice to always redirect upon a HTTP POST request. Thus +// one should use this method when HTTP POST validation failed and redirect +// the user back to the form. +func (v *Validation) Keep() { + v.keep = true +} + +// Clear *all* ValidationErrors +func (v *Validation) Clear() { + v.Errors = []*ValidationError{} +} + +// HasErrors returns true if there are any (ie > 0) errors. False otherwise. +func (v *Validation) HasErrors() bool { + return len(v.Errors) > 0 +} + +// ErrorMap returns the errors mapped by key. +// If there are multiple validation errors associated with a single key, the +// first one "wins". (Typically the first validation will be the more basic). +func (v *Validation) ErrorMap() map[string]*ValidationError { + m := map[string]*ValidationError{} + for _, e := range v.Errors { + if _, ok := m[e.Key]; !ok { + m[e.Key] = e + } + } + return m +} + +// Error adds an error to the validation context. +func (v *Validation) Error(message string, args ...interface{}) *ValidationResult { + result := v.ValidationResult(false).Message(message, args...) + v.Errors = append(v.Errors, result.Error) + return result +} + +// Error adds an error to the validation context. +func (v *Validation) ErrorKey(message string, args ...interface{}) *ValidationResult { + result := v.ValidationResult(false).MessageKey(message, args...) + v.Errors = append(v.Errors, result.Error) + return result +} + +// Error adds an error to the validation context. +func (v *Validation) ValidationResult(ok bool) *ValidationResult { + if ok { + return &ValidationResult{Ok: ok} + } else { + return &ValidationResult{Ok: ok, Error: &ValidationError{}, Locale: v.Request.Locale, Translator: v.Translator} + } +} + +// ValidationResult is returned from every validation method. +// It provides an indication of success, and a pointer to the Error (if any). +type ValidationResult struct { + Error *ValidationError + Ok bool + Locale string + Translator func(locale, message string, args ...interface{}) string +} + +// Key sets the ValidationResult's Error "key" and returns itself for chaining +func (r *ValidationResult) Key(key string) *ValidationResult { + if r.Error != nil { + r.Error.Key = key + } + return r +} + +// Message sets the error message for a ValidationResult. Returns itself to +// allow chaining. Allows Sprintf() type calling with multiple parameters +func (r *ValidationResult) Message(message string, args ...interface{}) *ValidationResult { + if r.Error != nil { + if len(args) == 0 { + r.Error.Message = message + } else { + r.Error.Message = fmt.Sprintf(message, args...) + } + } + return r +} + +// Allow a message key to be passed into the validation result. The Validation has already +// setup the translator to translate the message key +func (r *ValidationResult) MessageKey(message string, args ...interface{}) *ValidationResult { + if r.Error == nil { + return r + } + + // If translator found, use that to create the message, otherwise call Message method + if r.Translator != nil { + r.Error.Message = r.Translator(r.Locale, message, args...) + } else { + r.Message(message, args...) + } + + return r +} + +// Required tests that the argument is non-nil and non-empty (if string or list) +func (v *Validation) Required(obj interface{}) *ValidationResult { + return v.apply(Required{}, obj) +} + +func (v *Validation) Min(n int, min int) *ValidationResult { + return v.MinFloat(float64(n), float64(min)) +} + +func (v *Validation) MinFloat(n float64, min float64) *ValidationResult { + return v.apply(Min{min}, n) +} + +func (v *Validation) Max(n int, max int) *ValidationResult { + return v.MaxFloat(float64(n), float64(max)) +} + +func (v *Validation) MaxFloat(n float64, max float64) *ValidationResult { + return v.apply(Max{max}, n) +} + +func (v *Validation) Range(n, min, max int) *ValidationResult { + return v.RangeFloat(float64(n), float64(min), float64(max)) +} + +func (v *Validation) RangeFloat(n, min, max float64) *ValidationResult { + return v.apply(Range{Min{min}, Max{max}}, n) +} + +func (v *Validation) MinSize(obj interface{}, min int) *ValidationResult { + return v.apply(MinSize{min}, obj) +} + +func (v *Validation) MaxSize(obj interface{}, max int) *ValidationResult { + return v.apply(MaxSize{max}, obj) +} + +func (v *Validation) Length(obj interface{}, n int) *ValidationResult { + return v.apply(Length{n}, obj) +} + +func (v *Validation) Match(str string, regex *regexp.Regexp) *ValidationResult { + return v.apply(Match{regex}, str) +} + +func (v *Validation) Email(str string) *ValidationResult { + return v.apply(Email{Match{emailPattern}}, str) +} + +func (v *Validation) IPAddr(str string, cktype ...int) *ValidationResult { + return v.apply(IPAddr{cktype}, str) +} + +func (v *Validation) MacAddr(str string) *ValidationResult { + return v.apply(IPAddr{}, str) +} + +func (v *Validation) Domain(str string) *ValidationResult { + return v.apply(Domain{}, str) +} + +func (v *Validation) URL(str string) *ValidationResult { + return v.apply(URL{}, str) +} + +func (v *Validation) PureText(str string, m int) *ValidationResult { + return v.apply(PureText{m}, str) +} + +func (v *Validation) FilePath(str string, m int) *ValidationResult { + return v.apply(FilePath{m}, str) +} + +func (v *Validation) apply(chk Validator, obj interface{}) *ValidationResult { + if chk.IsSatisfied(obj) { + return v.ValidationResult(true) + } + + // Get the default key. + var key string + if pc, _, line, ok := runtime.Caller(2); ok { + f := runtime.FuncForPC(pc) + if defaultKeys, ok := DefaultValidationKeys[f.Name()]; ok { + key = defaultKeys[line] + } + } else { + utilLog.Error("Validation: Failed to get Caller information to look up Validation key") + } + + // Add the error to the validation context. + err := &ValidationError{ + Message: chk.DefaultMessage(), + Key: key, + } + v.Errors = append(v.Errors, err) + + // Also return it in the result. + vr := v.ValidationResult(false) + vr.Error = err + return vr +} + +// Check applies a group of validators to a field, in order, and return the +// ValidationResult from the first one that fails, or the last one that +// succeeds. +func (v *Validation) Check(obj interface{}, checks ...Validator) *ValidationResult { + var result *ValidationResult + for _, check := range checks { + result = v.apply(check, obj) + if !result.Ok { + return result + } + } + return result +} + +// ValidationFilter revel Filter function to be hooked into the filter chain. +func ValidationFilter(c *Controller, fc []Filter) { + // If json request, we shall assume json response is intended, + // as such no validation cookies should be tied response + if c.Params != nil && c.Params.JSON != nil { + c.Validation = &Validation{Request: c.Request, Translator: MessageFunc} + fc[0](c, fc[1:]) + } else { + errors, err := restoreValidationErrors(c.Request) + c.Validation = &Validation{ + Errors: errors, + keep: false, + Request: c.Request, + Translator: MessageFunc, + } + hasCookie := (err != http.ErrNoCookie) + + fc[0](c, fc[1:]) + + // Add Validation errors to ViewArgs. + c.ViewArgs["errors"] = c.Validation.ErrorMap() + + // Store the Validation errors + var errorsValue string + if c.Validation.keep { + for _, err := range c.Validation.Errors { + if err.Message != "" { + errorsValue += "\x00" + err.Key + ":" + err.Message + "\x00" + } + } + } + + // When there are errors from Validation and Keep() has been called, store the + // values in a cookie. If there previously was a cookie but no errors, remove + // the cookie. + if errorsValue != "" { + c.SetCookie(&http.Cookie{ + Name: CookiePrefix + "_ERRORS", + Value: url.QueryEscape(errorsValue), + Domain: CookieDomain, + Path: "/", + HttpOnly: true, + Secure: CookieSecure, + }) + } else if hasCookie { + c.SetCookie(&http.Cookie{ + Name: CookiePrefix + "_ERRORS", + MaxAge: -1, + Domain: CookieDomain, + Path: "/", + HttpOnly: true, + Secure: CookieSecure, + }) + } + } +} + +// Restore Validation.Errors from a request. +func restoreValidationErrors(req *Request) ([]*ValidationError, error) { + var ( + err error + cookie ServerCookie + errors = make([]*ValidationError, 0, 5) + ) + if cookie, err = req.Cookie(CookiePrefix + "_ERRORS"); err == nil { + ParseKeyValueCookie(cookie.GetValue(), func(key, val string) { + errors = append(errors, &ValidationError{ + Key: key, + Message: val, + }) + }) + } + return errors, err +} + +// DefaultValidationKeys register default validation keys for all calls to Controller.Validation.Func(). +// Map from (package).func => (line => name of first arg to Validation func) +// E.g. "myapp/controllers.helper" or "myapp/controllers.(*Application).Action" +// This is set on initialization in the generated main.go file. +var DefaultValidationKeys map[string]map[int]string