I'm using Constraints on my web forms and I've noticed that several forms have similar validations, for instance I have several types of form with a start date and an end date. In each case, I want to validate that the start date is before the end date. Here's the case class I'm creating from my form:
case class OrderSearchForm(orderId: Option[Int], startDate:Option[Long], endDate:Option[Long])
and my validation (Let's ignore the .get() for now):
def validateSearchDate = Constraint[OrderSearchForm]{
osf: OrderSearchForm => {
if (!osf.startDate.isEmpty && !osf.endDate.isEmpty && osf.startDate.get.compareTo(osf.endDate.get) > 0 )
Invalid("Begin Date is after End Date.")
else
Valid
}
}
Now, since I have lots of forms with a start date and an end date, I'd like to re-write my validation to work with all of the case classes representing these forms. I'm wondering whether the typeclass pattern can help me with this:
trait TwoDates[T] {
def twoDatesTuple(t: T): (Option[Long], Option[Long])
}
trait TwoDatesOSF extends TwoDates[OrderSearchForm] {
def twoDatesTuple(t: OrderSearchForm) = (t.startDate, t.endDate)
}
implicit object TwoDatesOSF extends trait TwoDatesOSF
def validateSearchDate = Constraint[TwoDates[_]] { t: TwoDates[_] => ... (as above)}
but applying does not work:
validateSearchDate(OrderSearchForm(None, None, None))
yields:
error: type mismatch; found : OrderSearchForm required:
TwoDates[_]
betweenDates(osf)
1) Can I write generic validations using typeclasses? If so, what am I doing wrong?
2) Can I write generic validations while AVOIDING using super-classes (i.e.
abstract class TwoDates(start: Option[Long], end:Option[Long])
case class OrderSearchForm(orderId: Option[String], startDate:Option[Long], endDate:Option[Long]) extends TwoDates(startDate, endDate)
which seems awkward once multiple validations are in play)
Thanks!
I think you can use structural types:
private type TwoDates = { def startDate: Option[Date]; def endDate: Option[Date] }
def validateTwoDates = Constraint[TwoDates] { osf: TwoDates =>
if (!osf.startDate.isEmpty &&
!osf.endDate.isEmpty &&
osf.startDate.get.compareTo(osf.endDate.get) > 0) {
Invalid("Begin Date is after End Date.")
} else Valid
}
case class Something(
startDate: Option[Date],
endDate: Option[Date],
name: String)
private val form = Form(mapping(
"startDate" -> optional(date),
"endDate" -> optional(date),
"name" -> text)
(Something.apply)(Something.unapply).verifying(validateTwoDates))
Related
I want to cache a result of a method only when the attribute of the result contains specific values. For example
Class APIOutput(code: Int, message: String)
sealed class Response<out T : Any> : Serializable {
data class Success<out T : Any>(val data: T) : Response<T>()
data class Error(val errorText: String, val errorCode: Int) : Response<Nothing>()
}
#Cacheable(
key = "api-key",
unless = "do something here"
)
fun doApicall(uniqueId: Long): Response<APIOutput> {
//make API call
val output = callAPI(uniqueId)
return Response.Success(output)
}
In the above method, I want to cache the response only when Response.Success.data.code == (long list of codes).
Please note, in the previous line data is nothing but APIOutput object. How could I achieve it using unless or any other approach. I was thinking of writing a function that takes a doApicall method result as input and would return true or false and call that method it as unless="call a method". But I'm not sure how to do it. Any help is highly appreciated.
You can specify an expression to be evaluated in unless using SpEL. The returned value is available as result so you can do something like -
#Cacheable(
key = "api-key",
unless = "#result!=null or #result.success.data.code!=200"
)
fun doApicall(uniqueId: Long): Response<APIOutput> {
//make API call
val output = callAPI(uniqueId)
return Response.Success(output)
}
You can even use Regex in SpEL and can create custom Expression parsers if the existing functionality is not enough for your usecase.
Thanks Yatharth and John! Below is the condition that worked for me. resultcodes in the below expression is a list
#Cacheable(
key = "api-key",
unless = "!(#result instanceof T(com.abc.Response\$Success))
or (#result instanceof T(com.abc.Response\$Success)
and !(T(com.abc.APIStatus).resultCodes.contains(#result.data.code)))"
)
fun doApicall(uniqueId: Long): Response<APIOutput> {
//make API call
val output = callAPI(uniqueId)
return Response.Success(output)
}
I try to validate a nested domain class instance on a command object.
Having the following command object
package demo
import grails.databinding.BindingFormat
class SaveEventCommand {
#BindingFormat('yyyy-MM-dd')
Date date
Refreshment refreshment
static constraints = {
date validator: { date -> date > new Date() + 3}
refreshment nullable: true
}
}
And having the following domain class with its own constraints
package demo
class Refreshment {
String food
String drink
Integer quantity
static constraints = {
food inList: ['food1', 'food2', 'food3']
drink nullable: true, inList: ['drink1', 'drink2', 'drink3']
quantity: min: 1
}
}
I need when refreshment is not nullable the command object validates the date property and check the corresponding restrictions in refreshment instance
For now try with this code in the controller:
def save(SaveEventCommand command) {
if (command.hasErrors() || !command.refreshment.validate()) {
respond ([errors: command.errors], view: 'create')
return
}
// Store logic goes here
}
Here through !command.refreshment.validate() I try to validate the refresh instance but I get the result that there are no errors, even when passing data that is not correct.
Thank you any guide and thank you for your time
I typically just include some code that will use a custom validator to kick off validation for any property that is composed of another command object. For example:
thePropertyInQuestion(nullable: true, validator: {val, obj, err ->
if (val == null) return
if (!val.validate()) {
val.errors.allErrors.each { e ->
err.rejectValue(
"thePropertyInQuestion.${e.arguments[0]}",
"${e.objectName}.${e.arguments[0]}.${e.code}",
e.arguments,
"${e.objectName}.${e.arguments[0]}.${e.code}"
)
}
}
})
This way it's pretty clear that I want validation to occur. Plus it moves all the errors up into the root errors collection which makes things super easy for me.
Two things I could think of:
Implement grails.validation.Validateable on your command object
What happens when you provide an invalid date? Can you see errors while validating?
I've found some limitations in Play Faramework default Validation.
My biggest limitation is uniqueness validation.
Let say I'm Validating user registration form and i want to check if passed login already exists.
To do so, i need to ask db to count users by name
UsersService.countByName(s: String): Future[Long]
Is there a posiblity to solve this problem using scalaz Validation and |#|?
case class RegistrationForm(login: String)
object RegistrationForm {
def nonEmptyLogin(login: String): ValidationNel[String, String] = {
if(login.isEmpty)
"validation.error.blank.login".failureNel
else
login.successNel
}
def isLoginUnique(login: String): Future[ValidationNel[String, String]] = {
???
}
def validate(registrationForm: RegistrationForm): Future[ValidationNel[String, RegistrationForm]] = {
nonEmptyLogin(registrationForm.login) |#|
isLoginUnique(registrationForm.login) {
(_) => registrationForm
}
}
}
How should I implement the isLoginUnique method?
I'm not sure if I wrote validdate method correctly either. I just wanted to show my vision of validation.
My goal is to validate User's fields within the object's applymethod before creating one effective User instance:
case class User(String userName, String password)
object User {
def apply(userValidator: UserValidator): ValidationNel[UserCreationFailure, User] = {
//call UserValidator's validate() method here and initialize effective User instance.
}
}
I chose to use Validation from Scalaz7 to accumulate potential illegal arguments / errors.
One drawback in the following code is that Scalaz7 API force me to make the validator creates itself the instance. However, by following Single-Responsibility principle, it's clearly not its role. Its role would be to just validate fields and to return some errors list.
Let's first present my actual code (for information, Empty**** objects are just some case object extending UserCreationFailure):
class UserValidator(val userName: String, val password: String)
extends CommonValidator[UserCreationFailure] {
def validate(): ValidationNel[UserCreationFailure, User] = {
(checkForUserName ⊛
checkForPassword)((userName, password) => new User(userName, password)
}
private def checkForUserName: ValidationNel[UserCreationFailure, String] = {
checkForNonEmptyString(userName) {
EmptyUserName
}
}
def checkForPassword: ValidationNel[UserCreationFailure, String] = {
checkForNonEmptyString(password) {
EmptyPassword
}
}
}
What I would expect is to merely return this snippet code:
(checkForUserName ⊛ checkForPassword)
and bring the appropriate result into my User class, allowing to create the effective instance by doing:
def apply(userValidator: UserValidator): ValidationNel[UserCreationFailure, User] = {
userValidator(username, password).validate()((userName, password)(new User(userName, password))
}
Indeed, it would be more friendly with SRP.
But (checkForUserName ⊛ checkForPassword) returns a totally private type type:
private[scalaz] trait ApplicativeBuilder[M[_], A, B],
thus I don't have the hand on the type of class returned.
Therefore, I am forced to directly associate User's creation with it.
How could I keep SRP and keep this validation mechanism?
-----UPDATE----
As #Travis Brown mentioned, the intent to use an external class for my UserValidator may seem weird. Actually, I expect the validator to be mockable and thus, I'm forced to use composition over trait/abstract class.
I'm not sure I understand why you need a dedicated UserValidator class in the first place. In a case like this I'd be more likely to bundle all of my generic validation code into a separate trait, and to have my User companion object (or whatever other piece I want to be responsible for creating User instances) extend that trait. Here's a quick sketch:
import scalaz._, Scalaz._
trait Validator[E] {
def checkNonEmpty(error: E)(s: String): ValidationNel[E, String] =
if (s.isEmpty) error.failNel else s.successNel
}
sealed trait UserCreationFailure
case object EmptyPassword extends UserCreationFailure
case object EmptyUsername extends UserCreationFailure
case class User(name: String, pass: String)
object User extends Validator[UserCreationFailure] {
def validated(
name: String,
pass: String
): ValidationNel[UserCreationFailure, User] = (
checkNonEmpty(EmptyUsername)(name) |#| checkNonEmpty(EmptyPassword)(pass)
)(apply)
}
And then:
scala> println(User.validated("", ""))
Failure(NonEmptyList(EmptyUsername, EmptyPassword))
scala> println(User.validated("a", ""))
Failure(NonEmptyList(EmptyPassword))
scala> println(User.validated("", "b"))
Failure(NonEmptyList(EmptyUsername))
scala> println(User.validated("a", "b"))
Success(User(a,b))
If you have a huge amount of User-specific validation logic that you don't want polluting your User object, I suppose you could factor it out into a UserValidator trait that would extend your generic Validator and be extended by User.
I have the following domain class in my grails project:
class Vacation {
Date start
Date end
User vacationer
static constraints = {
start(validator: {return (it >= new Date()-1)})
}
}
Is it possible to add a validator that requires end to be equal or greater than start?
Cheers
Use
start(validator: {
val, obj ->
val < obj.properties['end']
})
You can directly access property "end", since obj is the object of the class Vacation only, where it is defined.
Use:
start(validator: {
val, obj ->
val < obj.end
})