From fc2f37b4a3c22747d0e913a2b7a379dbe7e9e7d1 Mon Sep 17 00:00:00 2001 From: vlad Date: Fri, 20 Oct 2017 14:17:05 -0700 Subject: Slick support for any Refined types, JSON format and generator for NonEmptyName, Unit-tests --- .../scala/xyz/driver/core/database/database.scala | 36 ++++++++++++++++++++-- src/main/scala/xyz/driver/core/json.scala | 20 ++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) (limited to 'src/main/scala/xyz/driver') diff --git a/src/main/scala/xyz/driver/core/database/database.scala b/src/main/scala/xyz/driver/core/database/database.scala index d305eee..26c1027 100644 --- a/src/main/scala/xyz/driver/core/database/database.scala +++ b/src/main/scala/xyz/driver/core/database/database.scala @@ -10,6 +10,11 @@ import com.typesafe.config.Config package database { + import java.sql.SQLDataException + + import eu.timepit.refined.api.{Refined, Validate} + import eu.timepit.refined.refineV + trait Database { val profile: JdbcProfile val database: JdbcProfile#Backend#Database @@ -61,6 +66,33 @@ package database { } } + trait RefinedColumnTypes[T, Predicate] extends ColumnTypes { + import profile.api._ + implicit def `eu.timepit.refined.api.Refined`( + implicit columnType: BaseColumnType[T], + validate: Validate[T, Predicate]): BaseColumnType[T Refined Predicate] + } + + object RefinedColumnTypes { + trait RefinedValue[T, Predicate] extends RefinedColumnTypes[T, Predicate] { + import profile.api._ + override implicit def `eu.timepit.refined.api.Refined`( + implicit columnType: BaseColumnType[T], + validate: Validate[T, Predicate]): BaseColumnType[T Refined Predicate] = + MappedColumnType.base[T Refined Predicate, T]( + _.value, { dbValue => + refineV[Predicate](dbValue) match { + case Left(refinementError) => + throw new SQLDataException( + s"Value in the database doesn't match the refinement constraints: $refinementError") + case Right(refinedValue) => + refinedValue + } + } + ) + } + } + trait IdColumnTypes extends ColumnTypes { import profile.api._ implicit def `xyz.driver.core.Id.columnType`[T]: BaseColumnType[Id[T]] @@ -84,7 +116,7 @@ package database { import profile.api._ override implicit def `xyz.driver.core.Id.columnType`[T] = - MappedColumnType.base[Id[T], String](_.value, Id[T](_)) + MappedColumnType.base[Id[T], String](_.value, Id[T]) } } @@ -117,7 +149,7 @@ package database { MappedColumnType .base[Id[T], java.util.UUID](id => java.util.UUID.fromString(id.value), uuid => Id[T](uuid.toString)) def serialKeyMapper[T] = MappedColumnType.base[Id[T], Long](_.value.toLong, serialId => Id[T](serialId.toString)) - def naturalKeyMapper[T] = MappedColumnType.base[Id[T], String](_.value, Id[T](_)) + def naturalKeyMapper[T] = MappedColumnType.base[Id[T], String](_.value, Id[T]) } trait DatabaseObject extends ColumnTypes { diff --git a/src/main/scala/xyz/driver/core/json.scala b/src/main/scala/xyz/driver/core/json.scala index c14424d..6b27a9c 100644 --- a/src/main/scala/xyz/driver/core/json.scala +++ b/src/main/scala/xyz/driver/core/json.scala @@ -16,6 +16,7 @@ import xyz.driver.core.domain.{Email, PhoneNumber} import xyz.driver.core.time.Time import eu.timepit.refined.refineV import eu.timepit.refined.api.{Refined, Validate} +import eu.timepit.refined.collection.NonEmpty object json { import DefaultJsonProtocol._ @@ -232,6 +233,25 @@ object json { } } + def NonEmptyNameInPath[T]: PathMatcher1[NonEmptyName[T]] = new PathMatcher1[NonEmptyName[T]] { + def apply(path: Path) = path match { + case Path.Segment(segment, tail) => + refineV[NonEmpty](segment) match { + case Left(_) => Unmatched + case Right(nonEmptyString) => Matched(tail, Tuple1(NonEmptyName[T](nonEmptyString))) + } + case _ => Unmatched + } + } + + implicit def nonEmptyNameFormat[T](implicit nonEmptyStringFormat: JsonFormat[Refined[String, NonEmpty]]) = + new RootJsonFormat[NonEmptyName[T]] { + def write(name: NonEmptyName[T]) = JsString(name.value.value) + + def read(value: JsValue): NonEmptyName[T] = + NonEmptyName[T](nonEmptyStringFormat.read(value)) + } + val jsValueToStringMarshaller: Marshaller[JsValue, String] = Marshaller.strict[JsValue, String](value => Marshalling.Opaque[String](() => value.compactPrint)) -- cgit v1.2.3