Skip to content

Commit

Permalink
add test case for #30 and #31
Browse files Browse the repository at this point in the history
  • Loading branch information
oyvindberg committed Sep 8, 2023
1 parent ec4d5a2 commit cc0ea61
Show file tree
Hide file tree
Showing 23 changed files with 1,076 additions and 0 deletions.
14 changes: 14 additions & 0 deletions init/data/test-tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,17 @@ create table pgtestnull
numerics numeric[]
);

create extension citext;

CREATE TABLE users
(
user_id UUID NOT NULL,
name TEXT NOT NULL,
last_name TEXT NULL,
email CITEXT NOT NULL,
password TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
verified_on TIMESTAMPTZ NULL,
CONSTRAINT users_user_id_pk PRIMARY KEY (user_id),
CONSTRAINT users_email_unique UNIQUE (email)
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* File has been automatically generated by `typo`.
*
* IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN.
*/
package adventureworks
package customtypes

import anorm.Column
import anorm.ParameterMetaData
import anorm.ToStatement
import anorm.TypeDoesNotMatch
import java.sql.Types
import org.postgresql.jdbc.PgArray
import play.api.libs.json.Reads
import play.api.libs.json.Writes
import typo.dsl.Bijection

/** This is a type typo does not know how to handle yet. This falls back to casting to string and crossing fingers. Time to file an issue! :] */
case class TypoUnknownCitext(value: String)

object TypoUnknownCitext {
implicit lazy val arrayColumn: Column[Array[TypoUnknownCitext]] = Column.nonNull[Array[TypoUnknownCitext]]((v1: Any, _) =>
v1 match {
case v: PgArray =>
v.getArray match {
case v: Array[?] =>
Right(v.map(v => TypoUnknownCitext(v.asInstanceOf[String])))
case other => Left(TypeDoesNotMatch(s"Expected one-dimensional array from JDBC to produce an array of TypoUnknownCitext, got ${other.getClass.getName}"))
}
case other => Left(TypeDoesNotMatch(s"Expected instance of org.postgresql.jdbc.PgArray, got ${other.getClass.getName}"))
}
)
implicit lazy val arrayToStatement: ToStatement[Array[TypoUnknownCitext]] = ToStatement[Array[TypoUnknownCitext]]((s, index, v) => s.setArray(index, s.getConnection.createArrayOf("citext", v.map(v => v.value))))
implicit lazy val bijection: Bijection[TypoUnknownCitext, String] = Bijection[TypoUnknownCitext, String](_.value)(TypoUnknownCitext.apply)
implicit lazy val column: Column[TypoUnknownCitext] = Column.nonNull[TypoUnknownCitext]((v1: Any, _) =>
v1 match {
case v: String => Right(TypoUnknownCitext(v))
case other => Left(TypeDoesNotMatch(s"Expected instance of java.lang.String, got ${other.getClass.getName}"))
}
)
implicit lazy val ordering: Ordering[TypoUnknownCitext] = Ordering.by(_.value)
implicit lazy val parameterMetadata: ParameterMetaData[TypoUnknownCitext] = new ParameterMetaData[TypoUnknownCitext] {
override def sqlType: String = "citext"
override def jdbcType: Int = Types.OTHER
}
implicit lazy val reads: Reads[TypoUnknownCitext] = Reads.StringReads.map(TypoUnknownCitext.apply)
implicit lazy val toStatement: ToStatement[TypoUnknownCitext] = ToStatement[TypoUnknownCitext]((s, index, v) => s.setObject(index, v.value))
implicit lazy val writes: Writes[TypoUnknownCitext] = Writes.StringWrites.contramap(_.value)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* File has been automatically generated by `typo`.
*
* IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN.
*/
package adventureworks
package public
package users

import adventureworks.customtypes.TypoOffsetDateTime
import adventureworks.customtypes.TypoUnknownCitext
import typo.dsl.SqlExpr.Field
import typo.dsl.SqlExpr.IdField
import typo.dsl.SqlExpr.OptField

trait UsersFields[Row] {
val userId: IdField[UsersId, Row]
val name: Field[String, Row]
val lastName: OptField[String, Row]
val email: Field[TypoUnknownCitext, Row]
val password: Field[String, Row]
val createdAt: Field[TypoOffsetDateTime, Row]
val verifiedOn: OptField[TypoOffsetDateTime, Row]
}
object UsersFields extends UsersStructure[UsersRow](None, identity, (_, x) => x)

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* File has been automatically generated by `typo`.
*
* IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN.
*/
package adventureworks
package public
package users

import adventureworks.customtypes.TypoUUID
import anorm.Column
import anorm.ParameterMetaData
import anorm.ToStatement
import play.api.libs.json.Reads
import play.api.libs.json.Writes
import typo.dsl.Bijection

/** Type for the primary key of table `public.users` */
case class UsersId(value: TypoUUID) extends AnyVal
object UsersId {
implicit lazy val arrayColumn: Column[Array[UsersId]] = Column.columnToArray(column, implicitly)
implicit lazy val arrayToStatement: ToStatement[Array[UsersId]] = TypoUUID.arrayToStatement.contramap(_.map(_.value))
implicit lazy val bijection: Bijection[UsersId, TypoUUID] = Bijection[UsersId, TypoUUID](_.value)(UsersId.apply)
implicit lazy val column: Column[UsersId] = TypoUUID.column.map(UsersId.apply)
implicit def ordering(implicit O0: Ordering[TypoUUID]): Ordering[UsersId] = Ordering.by(_.value)
implicit lazy val parameterMetadata: ParameterMetaData[UsersId] = new ParameterMetaData[UsersId] {
override def sqlType: String = TypoUUID.parameterMetadata.sqlType
override def jdbcType: Int = TypoUUID.parameterMetadata.jdbcType
}
implicit lazy val reads: Reads[UsersId] = TypoUUID.reads.map(UsersId.apply)
implicit lazy val toStatement: ToStatement[UsersId] = TypoUUID.toStatement.contramap(_.value)
implicit lazy val writes: Writes[UsersId] = TypoUUID.writes.contramap(_.value)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* File has been automatically generated by `typo`.
*
* IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN.
*/
package adventureworks
package public
package users

import adventureworks.customtypes.TypoUnknownCitext
import java.sql.Connection
import typo.dsl.DeleteBuilder
import typo.dsl.SelectBuilder
import typo.dsl.UpdateBuilder

trait UsersRepo {
def delete(userId: UsersId)(implicit c: Connection): Boolean
def delete: DeleteBuilder[UsersFields, UsersRow]
def insert(unsaved: UsersRow)(implicit c: Connection): UsersRow
def insert(unsaved: UsersRowUnsaved)(implicit c: Connection): UsersRow
def select: SelectBuilder[UsersFields, UsersRow]
def selectAll(implicit c: Connection): List[UsersRow]
def selectById(userId: UsersId)(implicit c: Connection): Option[UsersRow]
def selectByIds(userIds: Array[UsersId])(implicit c: Connection): List[UsersRow]
def selectByUnique(email: TypoUnknownCitext)(implicit c: Connection): Option[UsersRow]
def update(row: UsersRow)(implicit c: Connection): Boolean
def update: UpdateBuilder[UsersFields, UsersRow]
def upsert(unsaved: UsersRow)(implicit c: Connection): UsersRow
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/**
* File has been automatically generated by `typo`.
*
* IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN.
*/
package adventureworks
package public
package users

import adventureworks.customtypes.Defaulted
import adventureworks.customtypes.TypoOffsetDateTime
import adventureworks.customtypes.TypoUnknownCitext
import anorm.NamedParameter
import anorm.ParameterMetaData
import anorm.ParameterValue
import anorm.RowParser
import anorm.SQL
import anorm.SimpleSql
import anorm.SqlStringInterpolation
import anorm.ToStatement
import java.sql.Connection
import typo.dsl.DeleteBuilder
import typo.dsl.SelectBuilder
import typo.dsl.SelectBuilderSql
import typo.dsl.UpdateBuilder

object UsersRepoImpl extends UsersRepo {
override def delete(userId: UsersId)(implicit c: Connection): Boolean = {
SQL"""delete from public.users where "user_id" = ${ParameterValue(userId, null, UsersId.toStatement)}""".executeUpdate() > 0
}
override def delete: DeleteBuilder[UsersFields, UsersRow] = {
DeleteBuilder("public.users", UsersFields)
}
override def insert(unsaved: UsersRow)(implicit c: Connection): UsersRow = {
SQL"""insert into public.users("user_id", "name", "last_name", "email", "password", "created_at", "verified_on")
values (${ParameterValue(unsaved.userId, null, UsersId.toStatement)}::uuid, ${ParameterValue(unsaved.name, null, ToStatement.stringToStatement)}, ${ParameterValue(unsaved.lastName, null, ToStatement.optionToStatement(ToStatement.stringToStatement, ParameterMetaData.StringParameterMetaData))}, ${ParameterValue(unsaved.email, null, TypoUnknownCitext.toStatement)}::citext, ${ParameterValue(unsaved.password, null, ToStatement.stringToStatement)}, ${ParameterValue(unsaved.createdAt, null, TypoOffsetDateTime.toStatement)}::timestamptz, ${ParameterValue(unsaved.verifiedOn, null, ToStatement.optionToStatement(TypoOffsetDateTime.toStatement, TypoOffsetDateTime.parameterMetadata))}::timestamptz)
returning "user_id", "name", "last_name", "email"::text, "password", "created_at"::text, "verified_on"::text
"""
.executeInsert(UsersRow.rowParser(1).single)

}
override def insert(unsaved: UsersRowUnsaved)(implicit c: Connection): UsersRow = {
val namedParameters = List(
Some((NamedParameter("user_id", ParameterValue(unsaved.userId, null, UsersId.toStatement)), "::uuid")),
Some((NamedParameter("name", ParameterValue(unsaved.name, null, ToStatement.stringToStatement)), "")),
Some((NamedParameter("last_name", ParameterValue(unsaved.lastName, null, ToStatement.optionToStatement(ToStatement.stringToStatement, ParameterMetaData.StringParameterMetaData))), "")),
Some((NamedParameter("email", ParameterValue(unsaved.email, null, TypoUnknownCitext.toStatement)), "::citext")),
Some((NamedParameter("password", ParameterValue(unsaved.password, null, ToStatement.stringToStatement)), "")),
Some((NamedParameter("verified_on", ParameterValue(unsaved.verifiedOn, null, ToStatement.optionToStatement(TypoOffsetDateTime.toStatement, TypoOffsetDateTime.parameterMetadata))), "::timestamptz")),
unsaved.createdAt match {
case Defaulted.UseDefault => None
case Defaulted.Provided(value) => Some((NamedParameter("created_at", ParameterValue(value, null, TypoOffsetDateTime.toStatement)), "::timestamptz"))
}
).flatten
val quote = '"'.toString
if (namedParameters.isEmpty) {
SQL"""insert into public.users default values
returning "user_id", "name", "last_name", "email"::text, "password", "created_at"::text, "verified_on"::text
"""
.executeInsert(UsersRow.rowParser(1).single)
} else {
val q = s"""insert into public.users(${namedParameters.map{case (x, _) => quote + x.name + quote}.mkString(", ")})
values (${namedParameters.map{ case (np, cast) => s"{${np.name}}$cast"}.mkString(", ")})
returning "user_id", "name", "last_name", "email"::text, "password", "created_at"::text, "verified_on"::text
"""
SimpleSql(SQL(q), namedParameters.map { case (np, _) => np.tupled }.toMap, RowParser.successful)
.executeInsert(UsersRow.rowParser(1).single)
}

}
override def select: SelectBuilder[UsersFields, UsersRow] = {
SelectBuilderSql("public.users", UsersFields, UsersRow.rowParser)
}
override def selectAll(implicit c: Connection): List[UsersRow] = {
SQL"""select "user_id", "name", "last_name", "email"::text, "password", "created_at"::text, "verified_on"::text
from public.users
""".as(UsersRow.rowParser(1).*)
}
override def selectById(userId: UsersId)(implicit c: Connection): Option[UsersRow] = {
SQL"""select "user_id", "name", "last_name", "email"::text, "password", "created_at"::text, "verified_on"::text
from public.users
where "user_id" = ${ParameterValue(userId, null, UsersId.toStatement)}
""".as(UsersRow.rowParser(1).singleOpt)
}
override def selectByIds(userIds: Array[UsersId])(implicit c: Connection): List[UsersRow] = {
SQL"""select "user_id", "name", "last_name", "email"::text, "password", "created_at"::text, "verified_on"::text
from public.users
where "user_id" = ANY(${userIds})
""".as(UsersRow.rowParser(1).*)

}
override def selectByUnique(email: TypoUnknownCitext)(implicit c: Connection): Option[UsersRow] = {
SQL"""select "email"::text
from public.users
where "email" = ${ParameterValue(email, null, TypoUnknownCitext.toStatement)}
""".as(UsersRow.rowParser(1).singleOpt)

}
override def update(row: UsersRow)(implicit c: Connection): Boolean = {
val userId = row.userId
SQL"""update public.users
set "name" = ${ParameterValue(row.name, null, ToStatement.stringToStatement)},
"last_name" = ${ParameterValue(row.lastName, null, ToStatement.optionToStatement(ToStatement.stringToStatement, ParameterMetaData.StringParameterMetaData))},
"email" = ${ParameterValue(row.email, null, TypoUnknownCitext.toStatement)}::citext,
"password" = ${ParameterValue(row.password, null, ToStatement.stringToStatement)},
"created_at" = ${ParameterValue(row.createdAt, null, TypoOffsetDateTime.toStatement)}::timestamptz,
"verified_on" = ${ParameterValue(row.verifiedOn, null, ToStatement.optionToStatement(TypoOffsetDateTime.toStatement, TypoOffsetDateTime.parameterMetadata))}::timestamptz
where "user_id" = ${ParameterValue(userId, null, UsersId.toStatement)}
""".executeUpdate() > 0
}
override def update: UpdateBuilder[UsersFields, UsersRow] = {
UpdateBuilder("public.users", UsersFields, UsersRow.rowParser)
}
override def upsert(unsaved: UsersRow)(implicit c: Connection): UsersRow = {
SQL"""insert into public.users("user_id", "name", "last_name", "email", "password", "created_at", "verified_on")
values (
${ParameterValue(unsaved.userId, null, UsersId.toStatement)}::uuid,
${ParameterValue(unsaved.name, null, ToStatement.stringToStatement)},
${ParameterValue(unsaved.lastName, null, ToStatement.optionToStatement(ToStatement.stringToStatement, ParameterMetaData.StringParameterMetaData))},
${ParameterValue(unsaved.email, null, TypoUnknownCitext.toStatement)}::citext,
${ParameterValue(unsaved.password, null, ToStatement.stringToStatement)},
${ParameterValue(unsaved.createdAt, null, TypoOffsetDateTime.toStatement)}::timestamptz,
${ParameterValue(unsaved.verifiedOn, null, ToStatement.optionToStatement(TypoOffsetDateTime.toStatement, TypoOffsetDateTime.parameterMetadata))}::timestamptz
)
on conflict ("user_id")
do update set
"name" = EXCLUDED."name",
"last_name" = EXCLUDED."last_name",
"email" = EXCLUDED."email",
"password" = EXCLUDED."password",
"created_at" = EXCLUDED."created_at",
"verified_on" = EXCLUDED."verified_on"
returning "user_id", "name", "last_name", "email"::text, "password", "created_at"::text, "verified_on"::text
"""
.executeInsert(UsersRow.rowParser(1).single)

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* File has been automatically generated by `typo`.
*
* IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN.
*/
package adventureworks
package public
package users

import adventureworks.customtypes.TypoUnknownCitext
import java.sql.Connection
import typo.dsl.DeleteBuilder
import typo.dsl.DeleteBuilder.DeleteBuilderMock
import typo.dsl.DeleteParams
import typo.dsl.SelectBuilder
import typo.dsl.SelectBuilderMock
import typo.dsl.SelectParams
import typo.dsl.UpdateBuilder
import typo.dsl.UpdateBuilder.UpdateBuilderMock
import typo.dsl.UpdateParams

class UsersRepoMock(toRow: Function1[UsersRowUnsaved, UsersRow],
map: scala.collection.mutable.Map[UsersId, UsersRow] = scala.collection.mutable.Map.empty) extends UsersRepo {
override def delete(userId: UsersId)(implicit c: Connection): Boolean = {
map.remove(userId).isDefined
}
override def delete: DeleteBuilder[UsersFields, UsersRow] = {
DeleteBuilderMock(DeleteParams.empty, UsersFields, map)
}
override def insert(unsaved: UsersRow)(implicit c: Connection): UsersRow = {
if (map.contains(unsaved.userId))
sys.error(s"id ${unsaved.userId} already exists")
else
map.put(unsaved.userId, unsaved)
unsaved
}
override def insert(unsaved: UsersRowUnsaved)(implicit c: Connection): UsersRow = {
insert(toRow(unsaved))
}
override def select: SelectBuilder[UsersFields, UsersRow] = {
SelectBuilderMock(UsersFields, () => map.values.toList, SelectParams.empty)
}
override def selectAll(implicit c: Connection): List[UsersRow] = {
map.values.toList
}
override def selectById(userId: UsersId)(implicit c: Connection): Option[UsersRow] = {
map.get(userId)
}
override def selectByIds(userIds: Array[UsersId])(implicit c: Connection): List[UsersRow] = {
userIds.flatMap(map.get).toList
}
override def selectByUnique(email: TypoUnknownCitext)(implicit c: Connection): Option[UsersRow] = {
map.values.find(v => email == v.email)
}
override def update(row: UsersRow)(implicit c: Connection): Boolean = {
map.get(row.userId) match {
case Some(`row`) => false
case Some(_) =>
map.put(row.userId, row)
true
case None => false
}
}
override def update: UpdateBuilder[UsersFields, UsersRow] = {
UpdateBuilderMock(UpdateParams.empty, UsersFields, map)
}
override def upsert(unsaved: UsersRow)(implicit c: Connection): UsersRow = {
map.put(unsaved.userId, unsaved)
unsaved
}
}
Loading

0 comments on commit cc0ea61

Please sign in to comment.