From 45101d206092e139937a211cc6f1b13e17e97233 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 21 Sep 2023 23:52:34 -0700 Subject: [PATCH] Script to copy our typo codecs to typo_generated package --- .../wiringbits/repositories/TypoCodecs.scala | 126 +++++++++++++++++ typo/GenerateTypoSQLDSL.scala | 130 ++---------------- .../wiringbits/typo_generated/package.scala | 16 +++ 3 files changed, 153 insertions(+), 119 deletions(-) create mode 100644 server/src/main/scala/net/wiringbits/repositories/TypoCodecs.scala diff --git a/server/src/main/scala/net/wiringbits/repositories/TypoCodecs.scala b/server/src/main/scala/net/wiringbits/repositories/TypoCodecs.scala new file mode 100644 index 00000000..99b7e995 --- /dev/null +++ b/server/src/main/scala/net/wiringbits/repositories/TypoCodecs.scala @@ -0,0 +1,126 @@ +package net.wiringbits.repositories + +// Do not import packages, because it is easier to move this code to typo package without imports +object TypoCodecs { + implicit def wrappedColumn[T <: net.wiringbits.webapp.common.models.WrappedString](implicit + f: String => T + ): anorm.Column[T] = + anorm.Column.nonNull[T] { (value, _) => + value match { + case string: String => Right(f(string)) + case _ => Left(anorm.TypeDoesNotMatch("Error parsing the email")) + } + } + + implicit def wrappedOrdering[T <: net.wiringbits.webapp.common.models.WrappedString]: scala.math.Ordering[T] = + scala.math.Ordering.by(_.string) + + implicit def wrappedToStatement[T <: net.wiringbits.webapp.common.models.WrappedString]: anorm.ToStatement[T] = + anorm.ToStatement[T]((s, index, v) => s.setObject(index, v.string)) + + implicit def wrappedParameterMetaData[T <: net.wiringbits.webapp.common.models.WrappedString](implicit + customSqlType: String = "VARCHAR" + ): anorm.ParameterMetaData[T] = new anorm.ParameterMetaData[T] { + override def sqlType: String = customSqlType + + override def jdbcType: Int = java.sql.Types.OTHER + } + + implicit def idColumn[T <: net.wiringbits.common.models.id.Id](implicit f: String => T): anorm.Column[T] = + anorm.Column.nonNull[T] { (value, _) => + value match { + case string: String => Right(f(string)) + case _ => Left(anorm.TypeDoesNotMatch("Error parsing the email")) + } + } + + implicit def idOrdering[T <: net.wiringbits.common.models.id.Id]: Ordering[T] = Ordering.by(_.value) + + implicit def idToStatement[T <: net.wiringbits.common.models.id.Id]: anorm.ToStatement[T] = + anorm.ToStatement[T]((s, index, v) => s.setObject(index, v.value)) + + implicit def idParameterMetaData[T <: net.wiringbits.common.models.id.Id](implicit + customSqlType: String + ): anorm.ParameterMetaData[T] = new anorm.ParameterMetaData[T] { + override def sqlType: String = customSqlType + + override def jdbcType: Int = java.sql.Types.OTHER + } + + @SuppressWarnings(Array("org.wartremover.warts.Null")) + private def timestamp[T](ts: java.sql.Timestamp)(f: java.sql.Timestamp => T): Either[anorm.SqlRequestError, T] = + Right( + if (ts == null) null.asInstanceOf[T] else f(ts) + ) + + private val timestamptzParser: java.time.format.DateTimeFormatter = new java.time.format.DateTimeFormatterBuilder() + .appendPattern("yyyy-MM-dd HH:mm:ss") + .appendFraction(java.time.temporal.ChronoField.MICRO_OF_SECOND, 0, 6, true) + .appendPattern("X") + .toFormatter + implicit val columnToInstant: anorm.Column[net.wiringbits.common.models.InstantCustom] = + anorm.Column.nonNull(instantValueTo(instantToInstantCustom)) + + private def instantToInstantCustom(instant: java.time.Instant): net.wiringbits.common.models.InstantCustom = + net.wiringbits.common.models.InstantCustom(instant) + + private def instantValueTo( + epoch: java.time.Instant => net.wiringbits.common.models.InstantCustom + )(value: Any, meta: anorm.MetaDataItem): Either[anorm.SqlRequestError, net.wiringbits.common.models.InstantCustom] = { + value match { + case date: java.time.LocalDateTime => Right(epoch(date.toInstant(java.time.ZoneOffset.UTC))) + case ts: java.sql.Timestamp => timestamp(ts)(t => epoch(t.toInstant)) + case date: java.util.Date => + Right(epoch(java.time.Instant.ofEpochMilli(date.getTime))) + case time: Long => + Right(epoch(java.time.Instant.ofEpochMilli(time))) + case anorm.TimestampWrapper1(ts) => timestamp(ts)(t => epoch(t.toInstant)) + case anorm.TimestampWrapper2(ts) => timestamp(ts)(t => epoch(t.toInstant)) + case string: String => + scala.util.Try( + net.wiringbits.common.models + .InstantCustom(java.time.OffsetDateTime.parse(string, timestamptzParser).toInstant) + ) match + case scala.util.Failure(_) => Left(anorm.TypeDoesNotMatch("Error parsing the instant")) + case scala.util.Success(value) => Right(value) + case _ => + Left(anorm.TypeDoesNotMatch("Error parsing the instant")) + } + } + + implicit val instantCustomOrdering: Ordering[net.wiringbits.common.models.InstantCustom] = Ordering.by(_.value) + implicit val instantCustomToStatement: anorm.ToStatement[net.wiringbits.common.models.InstantCustom] = + anorm.ToStatement[net.wiringbits.common.models.InstantCustom]((s, index, v) => s.setObject(index, v.value.toString)) + implicit val instantParameterMetaData: anorm.ParameterMetaData[net.wiringbits.common.models.InstantCustom] = + new anorm.ParameterMetaData[net.wiringbits.common.models.InstantCustom] { + override def sqlType: String = "TIMESTAMPTZ" + + override def jdbcType: Int = java.sql.Types.TIMESTAMP_WITH_TIMEZONE + } + + implicit def enumJobTypeColumn[T <: enumeratum.EnumEntry](implicit + withNameInsensitiveOption: String => Option[T] + ): anorm.Column[T] = + anorm.Column.nonNull[T] { (value, _) => + value match { + case string: String => + withNameInsensitiveOption(string) match + case Some(value) => Right(value) + case None => Left(anorm.TypeDoesNotMatch(s"Unknown enum: $string")) + case _ => Left(anorm.TypeDoesNotMatch("Error parsing the enum")) + } + } + + implicit def enumOrdering[T <: enumeratum.EnumEntry]: scala.math.Ordering[T] = + scala.math.Ordering.by(_.entryName) + + implicit def enumToStatement[T <: enumeratum.EnumEntry]: anorm.ToStatement[T] = + anorm.ToStatement[T]((s, index, v) => s.setObject(index, v.entryName)) + + implicit def enumParameterMetaData[T <: enumeratum.EnumEntry]: anorm.ParameterMetaData[T] = + new anorm.ParameterMetaData[T] { + override def sqlType: String = "TEXT" + + override def jdbcType: Int = java.sql.Types.VARCHAR + } +} diff --git a/typo/GenerateTypoSQLDSL.scala b/typo/GenerateTypoSQLDSL.scala index 648e9c37..7299d0bf 100644 --- a/typo/GenerateTypoSQLDSL.scala +++ b/typo/GenerateTypoSQLDSL.scala @@ -73,17 +73,14 @@ object GenerateTypoSQLDSL extends App { given conn: java.sql.Connection = // TODO: get connection from config java.sql.DriverManager.getConnection( - "jdbc:postgresql://localhost:5432/wiringbits_db?user=postgres&password=postgres" + "jdbc:postgresql://localhost:5432/wiringbits_db_v2?user=postgres&password=postgres" ) - // current folder, where you run the script from - val pwd = os.pwd - // destination folder. All files in this dir will be overwritten! - val targetDir = pwd / "typo" / "shared" / "src" / "main" / "scala" + val targetDir = os.pwd / "typo" / "shared" / "src" / "main" / "scala" // where typo will look for sql files - val sqlScriptsFolder = pwd / "server" / "src" / "main" / "resources" / "sql" + val sqlScriptsFolder = os.pwd / "server" / "src" / "main" / "resources" / "sql" val pkg = "net.wiringbits.typo_generated" @@ -103,120 +100,15 @@ object GenerateTypoSQLDSL extends App { def addCustomCodecs = { // TODO: get from server - val str = - """ - |implicit def wrappedColumn[T <: net.wiringbits.webapp.common.models.WrappedString](implicit - | f: String => T - | ): anorm.Column[T] = - | anorm.Column.nonNull[T] { (value, _) => - | value match { - | case string: String => Right(f(string)) - | case _ => Left(anorm.TypeDoesNotMatch("Error parsing the email")) - | } - | } - | implicit def wrappedOrdering[T <: net.wiringbits.webapp.common.models.WrappedString]: scala.math.Ordering[T] = - | scala.math.Ordering.by(_.string) - | implicit def wrappedToStatement[T <: net.wiringbits.webapp.common.models.WrappedString]: anorm.ToStatement[T] = - | anorm.ToStatement[T]((s, index, v) => s.setObject(index, v.string)) - | implicit def wrappedParameterMetaData[T <: net.wiringbits.webapp.common.models.WrappedString](implicit - | customSqlType: String = "VARCHAR" - | ): anorm.ParameterMetaData[T] = new anorm.ParameterMetaData[T] { - | override def sqlType: String = customSqlType - | - | override def jdbcType: Int = java.sql.Types.OTHER - | } - | implicit def idColumn[T <: net.wiringbits.common.models.id.Id](implicit f: String => T): anorm.Column[T] = - | anorm.Column.nonNull[T] { (value, _) => - | value match { - | case string: String => Right(f(string)) - | case _ => Left(anorm.TypeDoesNotMatch("Error parsing the email")) - | } - | } - | implicit def idOrdering[T <: net.wiringbits.common.models.id.Id]: Ordering[T] = Ordering.by(_.value) - | implicit def idToStatement[T <: net.wiringbits.common.models.id.Id]: anorm.ToStatement[T] = - | anorm.ToStatement[T]((s, index, v) => s.setObject(index, v.value)) - | implicit def idParameterMetaData[T <: net.wiringbits.common.models.id.Id](implicit - | customSqlType: String - | ): anorm.ParameterMetaData[T] = new anorm.ParameterMetaData[T] { - | override def sqlType: String = customSqlType - | - | override def jdbcType: Int = java.sql.Types.OTHER - | } - | @SuppressWarnings(Array("org.wartremover.warts.Null")) - | private def timestamp[T](ts: java.sql.Timestamp)(f: java.sql.Timestamp => T): Either[anorm.SqlRequestError, T] = - | Right( - | if (ts == null) null.asInstanceOf[T] else f(ts) - | ) - | private val timestamptzParser: java.time.format.DateTimeFormatter = new java.time.format.DateTimeFormatterBuilder() - | .appendPattern("yyyy-MM-dd HH:mm:ss") - | .appendFraction(java.time.temporal.ChronoField.MICRO_OF_SECOND, 0, 6, true) - | .appendPattern("X") - | .toFormatter - | implicit val columnToInstant: anorm.Column[net.wiringbits.common.models.InstantCustom] = - | anorm.Column.nonNull(instantValueTo(instantToInstantCustom)) - | private def instantToInstantCustom(instant: java.time.Instant): net.wiringbits.common.models.InstantCustom = - | net.wiringbits.common.models.InstantCustom(instant) - | private def instantValueTo( - | epoch: java.time.Instant => net.wiringbits.common.models.InstantCustom - | )(value: Any, meta: anorm.MetaDataItem): Either[anorm.SqlRequestError, net.wiringbits.common.models.InstantCustom] = { - | value match { - | case date: java.time.LocalDateTime => Right(epoch(date.toInstant(java.time.ZoneOffset.UTC))) - | case ts: java.sql.Timestamp => timestamp(ts)(t => epoch(t.toInstant)) - | case date: java.util.Date => - | Right(epoch(java.time.Instant.ofEpochMilli(date.getTime))) - | case time: Long => - | Right(epoch(java.time.Instant.ofEpochMilli(time))) - | case anorm.TimestampWrapper1(ts) => timestamp(ts)(t => epoch(t.toInstant)) - | case anorm.TimestampWrapper2(ts) => timestamp(ts)(t => epoch(t.toInstant)) - | case string: String => - | scala.util.Try( - | net.wiringbits.common.models - | .InstantCustom(java.time.OffsetDateTime.parse(string, timestamptzParser).toInstant) - | ) match - | case scala.util.Failure(_) => Left(anorm.TypeDoesNotMatch("Error parsing the instant")) - | case scala.util.Success(value) => Right(value) - | case _ => - | Left(anorm.TypeDoesNotMatch("Error parsing the instant")) - | } - | } - | implicit val instantCustomOrdering: Ordering[net.wiringbits.common.models.InstantCustom] = Ordering.by(_.value) - | implicit val instantCustomToStatement: anorm.ToStatement[net.wiringbits.common.models.InstantCustom] = - | anorm.ToStatement[net.wiringbits.common.models.InstantCustom]((s, index, v) => s.setObject(index, v.value.toString)) - | implicit val instantParameterMetaData: anorm.ParameterMetaData[net.wiringbits.common.models.InstantCustom] = - | new anorm.ParameterMetaData[net.wiringbits.common.models.InstantCustom] { - | override def sqlType: String = "TIMESTAMPTZ" - | - | override def jdbcType: Int = java.sql.Types.TIMESTAMP_WITH_TIMEZONE - | } - | implicit def enumJobTypeColumn[T <: enumeratum.EnumEntry](implicit - | withNameInsensitiveOption: String => Option[T] - | ): anorm.Column[T] = - | anorm.Column.nonNull[T] { (value, _) => - | value match { - | case string: String => - | withNameInsensitiveOption(string) match - | case Some(value) => Right(value) - | case None => Left(anorm.TypeDoesNotMatch(s"Unknown enum: $string")) - | case _ => Left(anorm.TypeDoesNotMatch("Error parsing the enum")) - | } - | } - | implicit def enumOrdering[T <: enumeratum.EnumEntry]: scala.math.Ordering[T] = - | scala.math.Ordering.by(_.entryName) - | implicit def enumToStatement[T <: enumeratum.EnumEntry]: anorm.ToStatement[T] = - | anorm.ToStatement[T]((s, index, v) => s.setObject(index, v.entryName)) - | implicit def enumParameterMetaData[T <: enumeratum.EnumEntry]: anorm.ParameterMetaData[T] = - | new anorm.ParameterMetaData[T] { - | override def sqlType: String = "TEXT" - | - | override def jdbcType: Int = java.sql.Types.VARCHAR - | } - |} - |""".stripMargin - - val dir = targetDir / os.RelPath(pkg.replace('.', '/')) / "package.scala" + val typoCustomCodecs = os.read(os.pwd / "server" / "src" / "main" / "scala" / "net" / "wiringbits" / "repositories" / "TypoCodecs.scala") + val firstOpenningBracket = typoCustomCodecs.indexOf("{") + // + 1 because we are skipping the openning bracket + val code = typoCustomCodecs.substring(firstOpenningBracket + 1) + + val typoPackageDir = targetDir / os.RelPath(pkg.replace('.', '/')) / "package.scala" // - 2 because we want to remove the '\n' at the end of the file and the last '}' - os.truncate(dir, os.size(dir) - 2) - os.write.append(dir, str) + os.truncate(typoPackageDir, os.size(typoPackageDir) - 2) + os.write.append(typoPackageDir, code) } runScript diff --git a/typo/shared/src/main/scala/net/wiringbits/typo_generated/package.scala b/typo/shared/src/main/scala/net/wiringbits/typo_generated/package.scala index 9008d989..3acc5da5 100644 --- a/typo/shared/src/main/scala/net/wiringbits/typo_generated/package.scala +++ b/typo/shared/src/main/scala/net/wiringbits/typo_generated/package.scala @@ -60,10 +60,13 @@ package object typo_generated { case _ => Left(anorm.TypeDoesNotMatch("Error parsing the email")) } } + implicit def wrappedOrdering[T <: net.wiringbits.webapp.common.models.WrappedString]: scala.math.Ordering[T] = scala.math.Ordering.by(_.string) + implicit def wrappedToStatement[T <: net.wiringbits.webapp.common.models.WrappedString]: anorm.ToStatement[T] = anorm.ToStatement[T]((s, index, v) => s.setObject(index, v.string)) + implicit def wrappedParameterMetaData[T <: net.wiringbits.webapp.common.models.WrappedString](implicit customSqlType: String = "VARCHAR" ): anorm.ParameterMetaData[T] = new anorm.ParameterMetaData[T] { @@ -71,6 +74,7 @@ package object typo_generated { override def jdbcType: Int = java.sql.Types.OTHER } + implicit def idColumn[T <: net.wiringbits.common.models.id.Id](implicit f: String => T): anorm.Column[T] = anorm.Column.nonNull[T] { (value, _) => value match { @@ -78,9 +82,12 @@ package object typo_generated { case _ => Left(anorm.TypeDoesNotMatch("Error parsing the email")) } } + implicit def idOrdering[T <: net.wiringbits.common.models.id.Id]: Ordering[T] = Ordering.by(_.value) + implicit def idToStatement[T <: net.wiringbits.common.models.id.Id]: anorm.ToStatement[T] = anorm.ToStatement[T]((s, index, v) => s.setObject(index, v.value)) + implicit def idParameterMetaData[T <: net.wiringbits.common.models.id.Id](implicit customSqlType: String ): anorm.ParameterMetaData[T] = new anorm.ParameterMetaData[T] { @@ -88,11 +95,13 @@ package object typo_generated { override def jdbcType: Int = java.sql.Types.OTHER } + @SuppressWarnings(Array("org.wartremover.warts.Null")) private def timestamp[T](ts: java.sql.Timestamp)(f: java.sql.Timestamp => T): Either[anorm.SqlRequestError, T] = Right( if (ts == null) null.asInstanceOf[T] else f(ts) ) + private val timestamptzParser: java.time.format.DateTimeFormatter = new java.time.format.DateTimeFormatterBuilder() .appendPattern("yyyy-MM-dd HH:mm:ss") .appendFraction(java.time.temporal.ChronoField.MICRO_OF_SECOND, 0, 6, true) @@ -100,8 +109,10 @@ package object typo_generated { .toFormatter implicit val columnToInstant: anorm.Column[net.wiringbits.common.models.InstantCustom] = anorm.Column.nonNull(instantValueTo(instantToInstantCustom)) + private def instantToInstantCustom(instant: java.time.Instant): net.wiringbits.common.models.InstantCustom = net.wiringbits.common.models.InstantCustom(instant) + private def instantValueTo( epoch: java.time.Instant => net.wiringbits.common.models.InstantCustom )(value: Any, meta: anorm.MetaDataItem): Either[anorm.SqlRequestError, net.wiringbits.common.models.InstantCustom] = { @@ -125,6 +136,7 @@ package object typo_generated { Left(anorm.TypeDoesNotMatch("Error parsing the instant")) } } + implicit val instantCustomOrdering: Ordering[net.wiringbits.common.models.InstantCustom] = Ordering.by(_.value) implicit val instantCustomToStatement: anorm.ToStatement[net.wiringbits.common.models.InstantCustom] = anorm.ToStatement[net.wiringbits.common.models.InstantCustom]((s, index, v) => s.setObject(index, v.value.toString)) @@ -134,6 +146,7 @@ package object typo_generated { override def jdbcType: Int = java.sql.Types.TIMESTAMP_WITH_TIMEZONE } + implicit def enumJobTypeColumn[T <: enumeratum.EnumEntry](implicit withNameInsensitiveOption: String => Option[T] ): anorm.Column[T] = @@ -146,10 +159,13 @@ package object typo_generated { case _ => Left(anorm.TypeDoesNotMatch("Error parsing the enum")) } } + implicit def enumOrdering[T <: enumeratum.EnumEntry]: scala.math.Ordering[T] = scala.math.Ordering.by(_.entryName) + implicit def enumToStatement[T <: enumeratum.EnumEntry]: anorm.ToStatement[T] = anorm.ToStatement[T]((s, index, v) => s.setObject(index, v.entryName)) + implicit def enumParameterMetaData[T <: enumeratum.EnumEntry]: anorm.ParameterMetaData[T] = new anorm.ParameterMetaData[T] { override def sqlType: String = "TEXT"