Skip to content

Commit

Permalink
Establish Vert.x 5 compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
oliver-brm committed Feb 24, 2024
1 parent 31650fc commit 3d55997
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 182 deletions.
Original file line number Diff line number Diff line change
@@ -1,84 +1,77 @@
package io.vertx.lang.scala.itest.db.jdbc

import io.vertx.lang.scala.itest.db.ToDoDatabase
import io.vertx.jdbcclient.JDBCPool
import io.vertx.lang.scala.itest.domain.ToDo
import io.vertx.lang.scala.itest.domain.ToDo.{Title, Date, Time, Note}
import io.vertx.lang.scala.ScalaVerticle
import scala.concurrent.Future
import io.vertx.sqlclient.Tuple
import scala.language.implicitConversions
import io.vertx.lang.scala.ImplicitConversions.vertxFutureToScalaFuture
import scala.jdk.CollectionConverters.*
import io.vertx.sqlclient.RowSet
import io.vertx.sqlclient.Row
import io.vertx.lang.scala.itest.db.jdbc.ToDoDatabaseVertxJdbc.{TODO_ID, DUE_DATE, DUE_TIME, NOTES, TITLE, TODO, toIdAndToDo}
import scala.util.Try
import java.time.LocalDate
import java.time.LocalTime
import io.vertx.lang.scala.itest.db.{ID, ToDoDatabase}
import io.vertx.lang.scala.itest.db.jdbc.ToDoDatabaseVertxJdbc.*
import io.vertx.lang.scala.itest.domain.ToDo
import io.vertx.lang.scala.itest.domain.ToDo.{Date, Note, Time, Title}
import io.vertx.sqlclient.{Pool, Row, RowSet, Tuple}

import java.time.{LocalDate, LocalTime}
import scala.annotation.tailrec
import io.vertx.lang.scala.itest.db.ID
import scala.concurrent.ExecutionContext
import java.util.stream.Collector
import io.vertx.lang.scala.itest.domain.title
import io.vertx.lang.scala.itest.db.jdbc.ToDoDatabaseVertxJdbc.asTuple
import io.vertx.lang.scala.itest.db.jdbc.ToDoDatabaseVertxJdbc.asVertxTuple
import scala.concurrent.{ExecutionContext, Future}
import scala.jdk.CollectionConverters.*
import scala.language.implicitConversions

class ToDoDatabaseJdbc(pool: JDBCPool)(using ExecutionContext) extends ToDoDatabase:
class ToDoDatabaseJdbc(pool: Pool)(using ExecutionContext) extends ToDoDatabase:

override def load(id: ID): Future[Option[ToDo]] =
override def load(id: ID): Future[Option[ToDo]] =
for {
maybeTodo <- pool.preparedQuery(s"SELECT $TODO_ID, $TITLE, $NOTES, $DUE_DATE, $DUE_TIME FROM $TODO WHERE $TODO_ID = ?")
.mapping(row => toIdAndToDo(row)._2)
.execute(Tuple.of(id))
.map(_.asScala.headOption)
maybeTodo <- pool
.preparedQuery(s"SELECT $TODO_ID, $TITLE, $NOTES, $DUE_DATE, $DUE_TIME FROM $TODO WHERE $TODO_ID = ?")
.mapping(row => toIdAndToDo(row)._2)
.execute(Tuple.of(id))
.map(_.asScala.headOption)
} yield maybeTodo

override def loadAll: Future[Map[ID, ToDo]] =
override def loadAll: Future[Map[ID, ToDo]] =
for {
todos <- pool.query(s"SELECT $TODO_ID, $TITLE, $NOTES, $DUE_DATE, $DUE_TIME FROM $TODO")
.mapping(toIdAndToDo)
.execute()
.map(_.iterator.asScala.toList.toMap)
todos <- pool
.query(s"SELECT $TODO_ID, $TITLE, $NOTES, $DUE_DATE, $DUE_TIME FROM $TODO")
.mapping(toIdAndToDo)
.execute()
.map(_.iterator.asScala.toList.toMap)
} yield todos

override def save(todo: ToDo): Future[ID] = pool.withTransaction { conn =>
override def save(todo: ToDo): Future[ID] = pool.withTransaction { conn =>
for {
id <- conn.preparedQuery(s"SELECT nextval('todo_id_seq') AS ID")
.execute()
.map(_.asScala.head.getLong("ID"))
_ <- conn.preparedQuery(s"INSERT INTO $TODO ($TODO_ID, $TITLE, $NOTES, $DUE_DATE, $DUE_TIME) VALUES ($id, ?, ?, ?, ?)")
.execute(asVertxTuple(todo))
id <- conn
.preparedQuery(s"SELECT nextval('todo_id_seq') AS ID")
.execute()
.map(_.asScala.head.getLong("ID"))
_ <- conn
.preparedQuery(s"INSERT INTO $TODO ($TODO_ID, $TITLE, $NOTES, $DUE_DATE, $DUE_TIME) VALUES ($id, ?, ?, ?, ?)")
.execute(asVertxTuple(todo))
} yield ID(id)
}

override def delete(id: ID): Future[Boolean] =
override def delete(id: ID): Future[Boolean] =
for {
affectedRows <- pool.preparedQuery(s"DELETE FROM $TODO WHERE $TODO_ID = ?")
.execute(Tuple.of(id))
.map(_.rowCount)
affectedRows <- pool
.preparedQuery(s"DELETE FROM $TODO WHERE $TODO_ID = ?")
.execute(Tuple.of(id))
.map(_.rowCount)
success = affectedRows == 1
} yield success



object ToDoDatabaseVertxJdbc:
val TODO_ID: String = "ID"
val TITLE: String = "TITLE"
val NOTES: String = "NOTES"
val TODO_ID: String = "ID"
val TITLE: String = "TITLE"
val NOTES: String = "NOTES"
val DUE_DATE: String = "DUE_DATE"
val DUE_TIME: String = "DUE_TIME"
val TODO = "TODO"
val TODO = "TODO"

def toIdAndToDo(row: Row): (ID, ToDo) =
// id and title must be there
val id = ID(row.getLong(TODO_ID))
val title = row.getString(TITLE)
val maybeNotes = Option(row.getString(NOTES))
val id = ID(row.getLong(TODO_ID))
val title = row.getString(TITLE)
val maybeNotes = Option(row.getString(NOTES))
val maybeDueDate = Option(row.getLocalDate(DUE_DATE))
val maybeDueTime = Option(row.getLocalTime(DUE_TIME))
(id -> ToDo(Title(title), maybeNotes, maybeDueDate, maybeDueTime))
id -> ToDo(Title(title), maybeNotes, maybeDueDate, maybeDueTime)

def asVertxTuple(todo: ToDo): Tuple =
def asVertxTuple(todo: ToDo): Tuple =
val (title, notes, date, time) = asTuple(todo)
Tuple.of(title, notes, date, time)

Expand All @@ -88,5 +81,3 @@ object ToDoDatabaseVertxJdbc:
case Note(note, todo) => asTuple(todo, acc.copy(_2 = note))
case Date(date, todo) => asTuple(todo, acc.copy(_3 = date))
case Time(time, todo) => asTuple(todo, acc.copy(_4 = time))


Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,55 @@ package io.vertx.lang.scala.itest.db.jdbc

import io.vertx.core.Vertx
import io.vertx.jdbcclient.JDBCPool
import io.vertx.lang.scala.ImplicitConversions.vertxFutureToScalaFuture
import io.vertx.lang.scala.ImplicitConversions.vertxFutureVoidToScalaFutureUnit
import io.vertx.lang.scala.VertxExecutionContext
import io.vertx.lang.scala.asScala
import io.vertx.lang.scala.itest.db.ID
import io.vertx.lang.scala.itest.db.ToDoDatabaseService
import io.vertx.lang.scala.itest.domain.ToDo
import io.vertx.lang.scala.itest.domain.ToDo.Date
import io.vertx.lang.scala.itest.domain.ToDo.Note
import io.vertx.lang.scala.itest.domain.ToDo.Time
import io.vertx.lang.scala.itest.domain.ToDo.Title
import io.vertx.lang.scala.itest.domain.title
import io.vertx.lang.scala.ImplicitConversions.{vertxFutureToScalaFuture, vertxFutureVoidToScalaFutureUnit}
import io.vertx.lang.scala.{asScala, VertxExecutionContext}
import io.vertx.lang.scala.itest.db.{ID, ToDoDatabaseService}
import io.vertx.lang.scala.itest.domain.ToDo.{Date, Note, Time, Title}
import io.vertx.lang.scala.itest.domain.{title, ToDo}
import io.vertx.lang.scala.json.Json
import io.vertx.scala.core.JsonObject
import org.scalatest.BeforeAndAfter
import io.vertx.sqlclient.Pool
import org.scalatest.Checkpoints.Checkpoint
import org.scalatest.FutureOutcome
import org.scalatest.ParallelTestExecution
import org.scalatest.Succeeded
import org.scalatest.{BeforeAndAfter, FutureOutcome, ParallelTestExecution, Succeeded}
import org.scalatest.compatible.Assertion
import org.scalatest.funspec.FixtureAsyncFunSpec
import org.scalatest.matchers.should.Matchers

import java.util.concurrent.TimeUnit
import scala.concurrent.Await
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.concurrent.duration.Duration
import scala.jdk.CollectionConverters.*
import scala.language.implicitConversions
import io.vertx.scala.jdbcclient.JDBCConnectOptions
import io.vertx.scala.sqlclient.PoolOptions

class ToDoDatabaseJdbcSpec extends FixtureAsyncFunSpec, Matchers, ParallelTestExecution:

type CUT = ToDoDatabaseJdbc
type FixtureParam = (CUT, JDBCPool)
type CUT = ToDoDatabaseJdbc
type FixtureParam = (CUT, Pool)

override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
val vertx = Vertx.vertx()
given ExecutionContext = (VertxExecutionContext(vertx, vertx.getOrCreateContext()))
val config = Json.obj(Map(
"url" -> s"jdbc:h2:mem:${test.name}",
"username" -> "sa",
"password" -> "",
))
val pool = JDBCPool.pool(vertx, config)
val cut = ToDoDatabaseJdbc(pool)

val vertx = Vertx.vertx()
given ExecutionContext = VertxExecutionContext(vertx, vertx.getOrCreateContext())
val connectOptions = JDBCConnectOptions(jdbcUrl = s"jdbc:h2:mem:${test.name}", user = "sa", password = "")
val poolOptions = PoolOptions(maxSize = 5)
val pool = JDBCPool.pool(vertx, connectOptions, poolOptions)
val cut = ToDoDatabaseJdbc(pool)

val dbSetup: Future[Unit] = for {
_ <- pool.query("CREATE TABLE todo (id LONG PRIMARY KEY, title VARCHAR(64) NOT NULL, notes VARCHAR(4096), due_date DATE, due_time TIME)").execute()
_ <- pool
.query(
"CREATE TABLE todo (id LONG PRIMARY KEY, title VARCHAR(64) NOT NULL, notes VARCHAR(4096), due_date DATE, due_time TIME)"
)
.execute()
_ <- pool.query("CREATE SEQUENCE todo_id_seq").execute()
_ <- pool.query("INSERT INTO todo VALUES (nextval('todo_id_seq'), 'First ToDo', NULL, NULL, NULL)").execute()
_ <- pool.query("INSERT INTO todo VALUES (nextval('todo_id_seq'), 'Second ToDo', 'This is the second', NULL, NULL)").execute()
_ <- pool.query("INSERT INTO todo VALUES (nextval('todo_id_seq'), 'Third ToDo', NULL, CURRENT_DATE, CURRENT_TIME)").execute()
_ <- pool
.query("INSERT INTO todo VALUES (nextval('todo_id_seq'), 'Second ToDo', 'This is the second', NULL, NULL)")
.execute()
_ <- pool
.query("INSERT INTO todo VALUES (nextval('todo_id_seq'), 'Third ToDo', NULL, CURRENT_DATE, CURRENT_TIME)")
.execute()
} yield ()
Await.ready(dbSetup, Duration(5, TimeUnit.SECONDS))

Expand All @@ -66,14 +62,13 @@ class ToDoDatabaseJdbcSpec extends FixtureAsyncFunSpec, Matchers, ParallelTestEx
}
}


describe("ToDoDatabaseJdbc") {
describe("Loading all ToDos") {
it("should load all ToDos") { (cut, _) =>
for {
allToDos <- cut.loadAll
allTitles = allToDos.values.map(_.title).toList
assertion = allTitles should contain inOrderOnly("First ToDo", "Second ToDo", "Third ToDo")
assertion = allTitles should contain inOrderOnly ("First ToDo", "Second ToDo", "Third ToDo")
} yield assertion
}

Expand All @@ -90,10 +85,12 @@ class ToDoDatabaseJdbcSpec extends FixtureAsyncFunSpec, Matchers, ParallelTestEx
it("should load ToDo #2 (Title, Note)") { (cut, _) =>
for {
maybeSecondToDo <- cut.load(ID(2))
assertion = maybeSecondToDo should matchPattern { case Some(Note("This is the second", Title("Second ToDo"))) => }
assertion = maybeSecondToDo should matchPattern {
case Some(Note("This is the second", Title("Second ToDo"))) =>
}
} yield assertion
}

it("should load ToDo #3 (Date, Time, Title)") { (cut, _) =>
for {
maybeThirdToDo <- cut.load(ID(3))
Expand All @@ -105,20 +102,21 @@ class ToDoDatabaseJdbcSpec extends FixtureAsyncFunSpec, Matchers, ParallelTestEx
describe("Saving ToDos") {
it("should save a ToDo and yield ID(4)") { (cut, _) =>
for {
id <- cut.save(Title("Fourth ToDo"))
id <- cut.save(Title("Fourth ToDo"))
assertion = id should be(4)
} yield assertion
}
}

describe("Deleting ToDos") {
it("should delete ToDo #2") { (cut, pool) =>
it("should delete ToDo #2") { (cut, pool) =>
val cp = Checkpoint()
for {
success <- cut.delete(ID(2))
rowCount <- pool.query("SELECT count(*) AS CNT FROM todo")
.execute()
.map(_.asScala.head.getInteger("CNT"))
rowCount <- pool
.query("SELECT count(*) AS CNT FROM todo")
.execute()
.map(_.asScala.head.getInteger("CNT"))
} yield {
cp { success should be(true) }
cp { rowCount should be(2) }
Expand All @@ -127,21 +125,20 @@ class ToDoDatabaseJdbcSpec extends FixtureAsyncFunSpec, Matchers, ParallelTestEx
}
}

it("should yield false if ToDo is not found") { (cut, pool) =>
it("should yield false if ToDo is not found") { (cut, pool) =>
val cp = Checkpoint()
for {
success <- cut.delete(ID(99))
rowCount <- pool.query("SELECT count(*) AS CNT FROM todo")
.execute()
.map(_.asScala.head.getInteger("CNT"))
rowCount <- pool
.query("SELECT count(*) AS CNT FROM todo")
.execute()
.map(_.asScala.head.getInteger("CNT"))
} yield {
cp { success should be(false) }
cp { rowCount should be(3) }
Succeeded
}
}
}

}


}
27 changes: 8 additions & 19 deletions vertx-lang-scala/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,6 @@
<artifactId>vertx-auth-otp</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-auth-jdbc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-auth-htdigest</artifactId>
Expand All @@ -110,11 +105,6 @@
<artifactId>vertx-auth-oauth2</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-auth-shiro</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-auth-webauthn</artifactId>
Expand Down Expand Up @@ -150,11 +140,6 @@
<artifactId>vertx-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-api-contract</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-templ-freemarker</artifactId>
Expand All @@ -167,7 +152,7 @@
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-templ-jade</artifactId>
<artifactId>vertx-web-templ-pug</artifactId>
<optional>true</optional>
</dependency>
<dependency>
Expand Down Expand Up @@ -350,6 +335,11 @@
<artifactId>vertx-openapi</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-openapi-router</artifactId>
<optional>true</optional>
</dependency>

<!-- Test dependencies -->
<dependency>
Expand Down Expand Up @@ -386,13 +376,11 @@
vertx-jdbc-client,
vertx-auth-common,
vertx-auth-otp
vertx-auth-jdbc,
vertx-auth-htdigest,
vertx-auth-htpasswd,
vertx-auth-jwt,
vertx-auth-mongo,
vertx-auth-oauth2,
vertx-auth-shiro,
vertx-auth-webauthn,
vertx-auth-properties,
vertx-auth-ldap,
Expand Down Expand Up @@ -441,7 +429,8 @@
vertx-micrometer-metrics,
vertx-tcp-eventbus-bridge,
vertx-json-schema,
vertx-openapi
vertx-openapi,
vertx-web-openapi-router
</includeArtifactIds>
<classifier>sources</classifier>
<includeTypes>jar</includeTypes>
Expand Down
Loading

0 comments on commit 3d55997

Please sign in to comment.