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.
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 have custom scalars for my users ID, CustomerID and ProviderID, I would like to validate them when someone call a mutation to ensure that the given ID match a user and of correct type.
We cannot make CustomerScalar parseValue method asynchronous, so I'm looking for a nice way to deal with such things.
Maybe customerDecorator ? I don't know ? Any idea ?
I would like to access my repository using Dependencies Injection to ensure that the passed ID exists in the database and is really a Customer.
Field Middleware and Directive seems not to support Deps injection.
#InputType()
export class CreateBillInput {
#Field(() => CustomerIDScalar)
customerID: CustomerID;
#Field()
name: string;
#Field(() => Int)
amount: number;
}
What I wanted that cannot work :
#Injectable()
export class CustomerIDScalar implements CustomScalar<string, CustomerID> {
constructor(private userRepository: IUserRepository) {}
parseValue(value: string) {
return this.getCustomerID(value);
}
parseLiteral(ast: ValueNode) {
if (ast.kind !== Kind.STRING) {
throw new TypeError('Argument is not a string value.');
}
return this.getCustomerID(value);;
}
serialize(value: CustomerID) {
return value.value; // value sent to the client
}
// TODO: Not usable here
private async getCustomerID(userID: string): Promise<CustomerID> {
const user = await this.userRepository.getByID(userID);
if (!user || !user.isCustomer()) {
throw new BadRequestException('There is no such customer user for provided ID.');
}
return user.id as CustomerID;
}
}
Thanks
First of all. gql scalars validation are about technical check to validate all data are correct (schema check). It's not suppose to have any business rules validation stuff there.
To achieve desired result you can use the next things:
Nest validation pipes with #Args decorator:
#Mutation(returns => Group)
async createGroup(
#Args('group', new ValidationPipe())
input: CreateGroupInput,
)
class-validation https://github.com/typestack/class-validator
Simply put a regular ID/Int scalar for the input type & validate it later in service class that responsive for such operation (recommend to use this approach for such things in gql)
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'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))
I'm using Play2 with anorm. I think the spirit of anorm is write plain sqls, no magic behind.
But I quickly found I have write a lot of similar dao methods. For example:
case class User(id:Pk[String], username:String, email:String, realname:String, city:String, website:String)
object User {
val simple = get[Pk[String]]("id") ~ get[String]("username") ~ ... get[String]("website") map {
case id ~ username ~ ... ~ website = User(id, username, ..., website)
}
def findByUsername(username:String) = DB.withConnection { implicit connection =>
SQL("select * from users where username={username}").on('username->username).as(simple.singleOpt)
}
def findByEmail(email:String) = DB.withConnection { implicit connection =>
SQL("select * from users where email={email}").on('email->email).as(simple.singleOpt)
}
def findById(id:String) = DB.withConnection { implicit connection =>
SQL("select * from users where id={id}").on('id->id).as(simple.singleOpt)
}
def findByRealname(keyword:String) = DB.withConnection { implicit connection =>
SQL("select * from users where realname like {keyword}").on('keyword->"%"+keyword+"%").as(simple *)
}
// more similar methods
}
There methods are almost the same, exception the where clause has small difference.
So I created a findWhere() method as:
def findWhere(conditon, values:Any*) = ...
That I can call it in actions:
User.findWhere("id=?", id)
User.findWhere("username=?", username)
It works, but I don't think it's recommended by anorm.
What's the best way to solve this problem?
Why do you believe it is not recommended or ok?
Anorm only cares of receiving a SQL query and parsing the result into a case class. If due to your constraints/design you generate that SQL request dinamically, that makes no difference.
The only issue I see is witht he '?' char, which is nto the way Anorm works. I believe it would me more like:
User.findWhere("username", username)
def findWhere(field: String, value: String) = {
SQL("select * from users where "+ field +"={"+ field +"}").on(Symbol(field)->value).as(simple.singleOpt)
}
This is a simple example, extend as required.