![]() |
|
![]() |
def deposit(amount : BigDecimal) = amount match {
case value if value> 0 => Right(AmountDeposited(value))
case _ => Left("invalid amount for deposit")
}
//tests
deposit(BigDecimal(-100)) mustBe Symbol("left")
deposit(BigDecimal(100)) mustBe Right(AmountDeposited(100))
import shapeless.tag._
import shapeless.tag
trait Positive
type PositiveInt = Int @@ Positive
def validate(value : PositiveInt) = value match {
case v if v <0 => Left(s"Predicate failed $v < 0")
case v => Right(v)
}
validate(tag[PositiveInt](10)) //Right(10)
validate(tag[PositiveInt](-10))//Left(Predicate failed -10<0)
Type Refined Predicate
Des erreurs remontées à la compilation
scala> val i: Int Refined Positive = -5
error: Predicate failed: (-5 > 0).
val i: Int Refined Positive = -5
^
ou au runtime
val j = refineMV[Positive](-5)
//j: Either[String, Int Refined Positive] =
// Left(Predicate failed: (-5 > 0).)
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._
def plusOne(a: Int Refined Positive) = a + 1
plusOne(42) // 43
def deposit(amount : BigDecimal Refined Positive) = {
AmountDeposited(value)
}
deposit(BigDecimal(100)) // compile
deposit(BigDecimal(-100)) //Predicate failed: (-100 > 0). 🙌
des URIS avec des verbes cohérents :
POST /users return 201
GET createUser return 200
{
"type": "string",
"minLength": 2,
"maxLength": 3
}
"street_type": {
"type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
}
def deposit(amount : BigDecimal Refined Positive) = {
AmountDeposited(value)
}
curl -X POST http://localhost:9000/login
-d '{"username" : "john.doe@yopmail.com",
"password" : ""}'
{
"obj.email":[
{"msg":["error.path.missing"]}
],
"obj.password":[
{"msg":["Predicate isEmpty() did not fail."]}
]
}
curl -X POST http://localhost:9000/login
-d '{"username" : "john.doe@yopmail.com",
"password" : ""}'
{
"_schema": {
"email": {
"minLength": 1,
"type": "string"
},
"password": {
"minLength": 1,
"type": "string"
}
},
...
}
resolvers += Resolver.bintrayRepo("dgouyette", "maven")
libraryDependencies +=
"org.dgouyette"%% "play-api-refiner"% "1.0.0-M1"
case class SimpleString(value : String)
JsonSchema.jsonSchema[SimpleString]
{
"value" : {
"type" : "string"
}
}
case class StringNonEmpty(value : String Refined NonEmpty)
JsonSchema.jsonSchema[StringNonEmpty]
{
"value":{
"minLength":1,
"type":"string"
}
}
case class CollectionIntNonEmpty(
value : List[Int] Refined NonEmpty)
JsonSchema.jsonSchema[CollectionIntNonEmpty]
{
"value":{
"minLength":1,
"type":"array",
"items":{
"type":"integer"
}
}
}
sealed trait TrafficLight
case object Red extends TrafficLight
case object Orange extends TrafficLight
case object Green extends TrafficLight
case class SimpleEnum(e : TrafficLight)
JsonSchema.jsonSchema[SimpleEnum]
{
"e":{
"enum":["red","orange","green"],
"type":"string"
}
}
case class Login(email : String Refined NonEmpty)
JsonSchema.asJsValue[SimpleString]
//{"email":{"minLength":1,"type":"string"}}
object JsonSchema {
def asJsValue[T]: JsValue = macro impl[T]
def impl[T: c.WeakTypeTag] = {
typeOf[T] // type de la classe = Login
.decls.collect {
//champs = List(email)
case m: MethodSymbol if m.isCaseAccessor =>
m.name // email
//eu.timepit.refined.api.Refined[T,P]
m.info.typeSymbol
//typeArgs=List(String,refined.collection.NonEmpty)
m.info.typeArgs
}
}
}
def extractArgs(typeArgs: Seq[Type])=typeArgs match {
case _refinedType :: _type :: _predicate :: Nil =>
extractArgs(List(_refinedType)) ++
extractArgs(List(_type)) ++
extractArgs(List(_predicate))
case _type :: Nil if _type =:= typeOf[String] =>
Json.obj("type" -> "string")
case _predicate :: Nil if _predicate =:= typeOf[NonEmpty] =>
Json.obj("minLength" -> 1)
}
case class Login(email : String Refined NonEmpty,
password : String Refined NonEmpty)
val loginFmt = Json.format[Login]
val loginSchema = JsonSchema.asJsValue[Login]
//Action[Login]
def login()=Action(bp.jsonRefined(loginFmt, loginSchema)) {
implicit request => Ok
}
POST /login controllers.HomeController.login
implicit def refinedR[T,P](
implicit readsT : Reads[T],
v : Validate[T,P]) : Reads[T Refined P] =
(json: JsValue) => {
readsT
.reads(json)
.flatMap { t: T =>
refineV[P](t) match {//validation refined au runtime
case Left(error) => JsError(error)
case Right(value) => JsSuccess(value)
}
}
}