Permalink
Fetching contributors…
Cannot retrieve contributors at this time
175 lines (153 sloc) 5.39 KB
package skinny.controller.feature
import skinny.micro.SkinnyMicroBase
import skinny.micro.context.SkinnyContext
import skinny.validator._
import skinny.validator.NewValidation
import skinny.validator.MapValidator
import skinny.I18n
import java.util.Locale
import skinny.controller.{ KeyAndErrorMessages, Params }
import skinny.util.StringUtil.toCamelCase
/**
* Validation support for Skinny app.
*/
trait ValidationFeature { self: SkinnyMicroBase with RequestScopeFeature with LocaleFeature =>
/**
* Creates new validation form.
*
* @param validations validations
* @param locale current locale
* @return validator
*/
def validationWithParams(validations: NewValidation*)(
implicit locale: Locale = currentLocale(context).orNull[Locale]
): MapValidator = {
validationWithPrefix(Params(params(context)), null, validations: _*)(context)
}
/**
* Creates new validation form.
*
* @param params params
* @param validations validations
* @param locale current locale
* @return validator
*/
def validation(params: Params, validations: NewValidation*)(
implicit locale: Locale = currentLocale(context).orNull[Locale]
): MapValidator = {
validationWithPrefix(params, null, validations: _*)(context)
}
/**
* Creates new validation form.
*
* @param prefix key prefix for error message
* @param params params
* @param validations validations
* @param locale current locale
* @return validator
*/
def validationWithParamsAndPrefix(prefix: String, params: Params, validations: NewValidation*)(
implicit locale: Locale = currentLocale(context).orNull[Locale]
): MapValidator = {
validationWithPrefix(params, prefix, validations: _*)(context)
}
/**
* Creates new validation form.
*
* @param params params
* @param prefix key prefix for error message
* @param validations validations
* @return validator
*/
def validationWithPrefix(params: Params, prefix: String, validations: NewValidation*)(
implicit ctx: SkinnyContext
): MapValidator = {
implicit val locale: Locale = currentLocale(ctx).orNull[Locale]
if (params == null) {
throw new IllegalStateException("You cannot call #validation when Scalatra's #params is absent.")
}
val validator = new MapValidator(params.underlying, Validations(params.underlying, validations)) {
override def validate(): Boolean = {
if (hasErrors) {
(status = 400)(ctx)
setParams(ctx)
}
super.validate()
}
}
validator
.success { _ =>
}
.failure { (inputs, errors: Errors) =>
// errorMessages
val errorMessages: Seq[String] = {
ValidationFeature.buildErrorMessagesWithPrefix(prefix, validations, errors)
}
set(RequestScopeFeature.ATTR_ERROR_MESSAGES, errorMessages)(ctx)
// keyAndErrorMessages
val keyAndErrorMessages: KeyAndErrorMessages = {
ValidationFeature.buildKeyAndErrorMessagesWithPrefix(prefix, validations, errors)
}
set(RequestScopeFeature.ATTR_KEY_AND_ERROR_MESSAGES, keyAndErrorMessages)(ctx)
}
.apply()
validator
}
}
object ValidationFeature {
def buildErrorMessages(validations: Seq[NewValidation], errors: Errors)(
implicit locale: Locale
): Seq[String] = {
buildErrorMessagesWithPrefix(null, validations, errors)
}
def buildErrorMessagesWithPrefix(prefix: String, validations: Seq[NewValidation], errors: Errors)(
implicit locale: Locale
): Seq[String] = {
val skinnyValidationMessages = Messages.loadFromConfig(locale = Option(locale))
val i18n = I18n(locale)
def withPrefix(key: String): String = if (prefix != null) s"${prefix}.${key}" else key
validations.map(_.paramDef.key).flatMap { key =>
errors.get(key).map { error =>
skinnyValidationMessages
.get(
key = error.name,
params = i18n.get(withPrefix(toCamelCase(key))).getOrElse(key) :: error.messageParams.map {
case I18nKeyParam(key) => i18n.getOrKey(key)
case value => value
}.toList
)
.getOrElse(error.name)
}
}
}
def buildKeyAndErrorMessages(validations: Seq[NewValidation], errors: Errors)(
implicit locale: Locale
): KeyAndErrorMessages = {
buildKeyAndErrorMessagesWithPrefix(null, validations, errors)
}
def buildKeyAndErrorMessagesWithPrefix(prefix: String, validations: Seq[NewValidation], errors: Errors)(
implicit locale: Locale
): KeyAndErrorMessages = {
val skinnyValidationMessages = Messages.loadFromConfig(locale = Option(locale))
val i18n = I18n(locale)
def withPrefix(key: String): String = if (prefix != null) s"${prefix}.${key}" else key
KeyAndErrorMessages(
validations
.map(_.paramDef.key)
.map { key =>
key -> errors.get(key).map { error =>
skinnyValidationMessages
.get(
key = error.name,
params = i18n.get(withPrefix(toCamelCase(key))).getOrElse(key) :: error.messageParams.map {
case I18nKeyParam(key) => i18n.getOrKey(key)
case value => value
}.toList
)
.getOrElse(error.name)
}
}
.toMap
)
}
}