From 91f4e393c16f81415ede67a018ffb2de509548b6 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 30 Nov 2023 13:41:53 +0100 Subject: [PATCH 01/90] #99: DBEngine requirement removed from DBSchema. Fixes #99. --- .../main/scala/za/co/absa/fadb/DBSchema.scala | 55 +++++++------------ .../za/co/absa/fadb/DBFunctionSuite.scala | 4 +- .../scala/za/co/absa/fadb/DBSchemaSuite.scala | 14 +---- 3 files changed, 24 insertions(+), 49 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBSchema.scala b/core/src/main/scala/za/co/absa/fadb/DBSchema.scala index c74decdf..bf2933ed 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBSchema.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBSchema.scala @@ -19,60 +19,45 @@ package za.co.absa.fadb import za.co.absa.fadb.naming.NamingConvention /** - * An abstract class, an ancestor to represent a database schema (each database function should be placed in a schema) + * An abstract class, an ancestor to represent a database schema * The database name of the schema is derived from the class name based on the provided naming convention * @param schemaNameOverride - in case the class name would not match the database schema name, this gives the - * possibility of override - * @param dBEngine - [[DBEngine]] to execute the functions with. Not directly needed for the DBSchema class, rather - * to be passed on to [[DBFunction]] members of the schema - * @param namingConvention - the [[za.co.absa.fadb.naming.NamingConvention NamingConvention]] prescribing how to convert a class name into a db object name + * @param namingConvention - the [[za.co.absa.fadb.naming.NamingConvention NamingConvention]] + * prescribing how to convert a class name into a db object name */ -abstract class DBSchema(schemaNameOverride: Option[String] = None) - (implicit dBEngine: DBEngine, implicit val namingConvention: NamingConvention) { +abstract class DBSchema(schemaNameOverride: Option[String] = None)(implicit val namingConvention: NamingConvention) +{ - def this(schemaNameOverride: String) - (implicit dBEngine: DBEngine, namingConvention: NamingConvention) { - this(Option(schemaNameOverride))(dBEngine, namingConvention) + def this(schemaNameOverride: String)(implicit namingConvention: NamingConvention) { + this(Option(schemaNameOverride))(namingConvention) } - def this(dBEngine: DBEngine, schemaNameOverride: String) - (implicit namingConvention: NamingConvention) { - this(Option(schemaNameOverride))(dBEngine, namingConvention) + def this()(implicit namingConvention: NamingConvention) { + this(None)(namingConvention) } - def this(dBEngine: DBEngine) - (implicit namingConvention: NamingConvention) { - this(None)(dBEngine, namingConvention) - } - - def this(namingConvention: NamingConvention, schemaNameOverride:String) - (implicit dBEngine: DBEngine) { - this(Option(schemaNameOverride))(dBEngine, namingConvention) - } - - def this(namingConvention: NamingConvention) - (implicit dBEngine: DBEngine) { - this(None)(dBEngine, namingConvention) + def this(namingConvention: NamingConvention, schemaNameOverride: String) { + this(Option(schemaNameOverride))(namingConvention) } /** - * To easy pass over to [[DBFunction]] members of the schema - */ + * To easy pass over to [[DBFunction]] members of the schema + */ protected implicit val schema: DBSchema = this /** - * Function to convert a class to the associated DB object name, based on the class' name. For transformation from the - * class name to usual db name the schema's [[za.co.absa.fadb.naming.NamingConvention NamingConvention]] is used. - * @param c - class which name to use to get the DB object name - * @return - the db object name - */ + * Function to convert a class to the associated DB object name, based on the class' name. For transformation from the + * class name to usual db name the schema's [[za.co.absa.fadb.naming.NamingConvention NamingConvention]] is used. + * @param c - class which name to use to get the DB object name + * @return - the db object name + */ def objectNameFromClassName(c: Class[_]): String = { namingConvention.fromClassNamePerConvention(c) } /** - * Name of the schema. Based on the schema's class name or provided override - */ + * Name of the schema. Based on the schema's class name or provided override + */ val schemaName: String = schemaNameOverride.getOrElse(objectNameFromClassName(getClass)) } diff --git a/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala b/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala index a19c9c09..47c62436 100644 --- a/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala @@ -33,8 +33,8 @@ class DBFunctionSuite extends AnyFunSuite { override implicit val executor: ExecutionContext = ExecutionContext.Implicits.global } - private object FooNamed extends DBSchema(EngineThrow) - private object FooNameless extends DBSchema(EngineThrow, "") + private object FooNamed extends DBSchema + private object FooNameless extends DBSchema("") test("Function name check"){ case class MyFunction(override val schema: DBSchema) extends DBFunction[Unit, Unit, DBEngine](schema) { diff --git a/core/src/test/scala/za/co/absa/fadb/DBSchemaSuite.scala b/core/src/test/scala/za/co/absa/fadb/DBSchemaSuite.scala index d72f49fa..8e7a90e4 100644 --- a/core/src/test/scala/za/co/absa/fadb/DBSchemaSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/DBSchemaSuite.scala @@ -18,27 +18,17 @@ package za.co.absa.fadb import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention -import scala.concurrent.{ExecutionContext, Future} - class DBSchemaSuite extends AnyFunSuite { - private object EngineThrow extends DBEngine { - override def run[R](query: QueryType[R]): Future[Seq[R]] = { - throw new Exception("Should never get here") - } - - override implicit val executor: ExecutionContext = ExecutionContext.Implicits.global - } - test("schema name default") { - class Foo extends DBSchema(EngineThrow) + class Foo extends DBSchema val schema = new Foo assert(schema.schemaName == "foo") } test("schema name overridden") { - class Foo extends DBSchema(EngineThrow, "bar") + class Foo extends DBSchema("bar") val schema = new Foo assert(schema.schemaName == "bar") From 5045b8a307ef5ade631ea110c17b55bec62223e2 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 1 Dec 2023 11:12:12 +0100 Subject: [PATCH 02/90] #99: Added DBSchema tests in order to increase code coverage. --- .../scala/za/co/absa/fadb/DBSchemaSuite.scala | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/core/src/test/scala/za/co/absa/fadb/DBSchemaSuite.scala b/core/src/test/scala/za/co/absa/fadb/DBSchemaSuite.scala index 8e7a90e4..16300efe 100644 --- a/core/src/test/scala/za/co/absa/fadb/DBSchemaSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/DBSchemaSuite.scala @@ -16,6 +16,7 @@ package za.co.absa.fadb import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.naming.NamingConvention import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention class DBSchemaSuite extends AnyFunSuite { @@ -34,4 +35,24 @@ class DBSchemaSuite extends AnyFunSuite { assert(schema.schemaName == "bar") } + test("schema name with naming convention without override") { + object LowerCaseNamingConvention extends NamingConvention { + def stringPerConvention(original: String): String = original.toLowerCase + } + class Bar extends DBSchema(LowerCaseNamingConvention, null) + + val schema = new Bar + assert(schema.schemaName == "bar") // Assuming the naming convention converts "Bar" to "bar" + } + + test("schema name with naming convention with override") { + object LowerCaseNamingConvention extends NamingConvention { + def stringPerConvention(original: String): String = original.toLowerCase + } + class Bar extends DBSchema(LowerCaseNamingConvention, "bar") + + val schema = new Bar + assert(schema.schemaName == "bar") + } + } From 15e96036b8f566ab9638b3bf8aa63438f1512f6b Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sun, 3 Dec 2023 00:02:51 +0100 Subject: [PATCH 03/90] tmp code of doobie integration --- build.sbt | 17 +- .../main/scala/za/co/absa/fadb/DBEngine.scala | 16 +- .../scala/za/co/absa/fadb/DBFunction.scala | 129 ++------- .../za/co/absa/fadb/DBFunctionSuite.scala | 126 ++++----- .../co/absa/fadb/doobie/DoobiePgEngine.scala | 17 ++ .../za/co/absa/fadb/doobie/DoobieQuery.scala | 9 + .../doobie/DoobieSingleResultFunction.scala | 19 ++ .../scala/za/co/absa/fadb/doobie/MyTest.scala | 54 ++++ .../examples/enceladus/DatasetSchema.scala | 244 +++++++++--------- project/Dependencies.scala | 11 +- .../za/co/absa/fadb/slick/SlickPgEngine.scala | 2 +- 11 files changed, 335 insertions(+), 309 deletions(-) create mode 100644 doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala create mode 100644 doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala create mode 100644 doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunction.scala create mode 100644 doobie/src/test/scala/za/co/absa/fadb/doobie/MyTest.scala diff --git a/build.sbt b/build.sbt index f1b83e62..9de2063d 100644 --- a/build.sbt +++ b/build.sbt @@ -22,7 +22,7 @@ ThisBuild / organization := "za.co.absa.fa-db" lazy val scala211 = "2.11.12" lazy val scala212 = "2.12.17" -ThisBuild / scalaVersion := scala211 +ThisBuild / scalaVersion := scala212 ThisBuild / crossScalaVersions := Seq(scala211, scala212) ThisBuild / versionScheme := Some("early-semver") @@ -88,6 +88,21 @@ lazy val faDBSlick = (project in file("slick")) jacocoExcludes := commonJacocoExcludes ) +lazy val faDBDoobie = (project in file("doobie")) + .configs(IntegrationTest) + .settings( + name := "doobie", + libraryDependencies ++= doobieDependencies(scalaVersion.value), + javacOptions ++= commonJavacOptions, + scalacOptions ++= commonScalacOptions, + (Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, // printScalaVersion is run with compile + Defaults.itSettings, + ).dependsOn(faDbCore) + .settings( + jacocoReportSettings := commonJacocoReportSettings.withTitle(s"doobie:slick Jacoco Report - scala:${scalaVersion.value}"), + jacocoExcludes := commonJacocoExcludes + ) + lazy val faDBExamples = (project in file("examples")) .configs(IntegrationTest) .settings( diff --git a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala index 45fe474a..06b75cab 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala @@ -16,13 +16,15 @@ package za.co.absa.fadb -import scala.concurrent.{ExecutionContext, Future} +import cats.Monad +import cats.implicits.toFunctorOps + import scala.language.higherKinds /** * A basis to represent a database executor */ -trait DBEngine { +abstract class DBEngine[F[_]: Monad] { /** * A type representing the (SQL) query within the engine @@ -30,15 +32,13 @@ trait DBEngine { */ type QueryType[T] <: Query[T] - implicit val executor: ExecutionContext - /** * The actual query executioner of the queries of the engine * @param query - the query to execute * @tparam R - return the of the query * @return - sequence of the results of database query */ - protected def run[R](query: QueryType[R]): Future[Seq[R]] + protected def run[R](query: QueryType[R]): F[Seq[R]] /** * Public method to execute when query is expected to return multiple results @@ -46,7 +46,7 @@ trait DBEngine { * @tparam R - return the of the query * @return - sequence of the results of database query */ - def fetchAll[R](query: QueryType[R]): Future[Seq[R]] = run(query) + def fetchAll[R](query: QueryType[R]): F[Seq[R]] = run(query) /** * Public method to execute when query is expected to return exactly one row @@ -54,7 +54,7 @@ trait DBEngine { * @tparam R - return the of the query * @return - sequence of the results of database query */ - def fetchHead[R](query: QueryType[R]): Future[R] = { + def fetchHead[R](query: QueryType[R]): F[R] = { run(query).map(_.head) } @@ -65,7 +65,7 @@ trait DBEngine { * @return - sequence of the results of database query */ - def fetchHeadOption[R](query: QueryType[R]): Future[Option[R]] = { + def fetchHeadOption[R](query: QueryType[R]): F[Option[R]] = { run(query).map(_.headOption) } } diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 0e2a65c6..04763864 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -16,8 +16,10 @@ package za.co.absa.fadb +import cats.Monad import za.co.absa.fadb.naming.NamingConvention -import scala.concurrent.Future + +import scala.language.higherKinds /** * @@ -30,35 +32,11 @@ import scala.concurrent.Future * @tparam R - the type covering the returned fields from the database function * @tparam E - the type of the [[DBEngine]] engine */ -abstract class DBFunction[I, R, E <: DBEngine](functionNameOverride: Option[String] = None) +abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) (implicit val schema: DBSchema, val dBEngine: E) extends DBFunctionFabric { /* alternative constructors for different availability of input parameters */ - def this(functionNameOverride: String) - (implicit schema: DBSchema, dBEngine: E) = { - this(Option(functionNameOverride))(schema, dBEngine) - } - - def this(schema: DBSchema, functionNameOverride: String) - (implicit dBEngine: E) = { - this(Option(functionNameOverride))(schema, dBEngine) - } - - /* only one constructor of a class can have default values for parameters*/ - def this(schema: DBSchema) - (implicit dBEngine: E) = { - this(None)(schema, dBEngine) - } - def this(dBEngine: E, functionNameOverride: String) - (implicit schema: DBSchema) = { - this(Option(functionNameOverride))(schema, dBEngine) - } - - def this(dBEngine: E) - (implicit schema: DBSchema) = { - this(None)(schema, dBEngine) - } /** * Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the @@ -89,9 +67,9 @@ abstract class DBFunction[I, R, E <: DBEngine](functionNameOverride: Option[Stri override protected def fieldsToSelect: Seq[String] = super.fieldsToSelect //TODO should get the names from R #6 /*these 3 functions has to be defined here and not in the ancestors, as there the query type is not compatible - path-dependent types*/ - protected def multipleResults(values: I): Future[Seq[R]] = dBEngine.fetchAll(query(values)) - protected def singleResult(values: I): Future[R] = dBEngine.fetchHead(query(values)) - protected def optionalResult(values: I): Future[Option[R]] = dBEngine.fetchHeadOption(query(values)) + protected def multipleResults(values: I): F[Seq[R]] = dBEngine.fetchAll(query(values)) + protected def singleResult(values: I): F[R] = dBEngine.fetchHead(query(values)) + protected def optionalResult(values: I): F[Option[R]] = dBEngine.fetchHeadOption(query(values)) } @@ -107,34 +85,9 @@ object DBFunction { * @tparam R - the type covering the returned fields from the database function * @tparam E - the type of the [[DBEngine]] engine */ - abstract class DBMultipleResultFunction[I, R, E <: DBEngine](functionNameOverride: Option[String] = None) + abstract class DBMultipleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) (implicit schema: DBSchema, dBEngine: E) - extends DBFunction[I, R, E](functionNameOverride) { - - def this(functionNameOverride: String) - (implicit schema: DBSchema, dBEngine: E) = { - this(Option(functionNameOverride))(schema, dBEngine) - } - - def this(schema: DBSchema, functionNameOverride: String) - (implicit dBEngine: E) = { - this(Option(functionNameOverride))(schema, dBEngine) - } - - def this(schema: DBSchema) - (implicit dBEngine: E) = { - this(None)(schema, dBEngine) - } - - def this(dBEngine: E, functionNameOverride: String) - (implicit schema: DBSchema) = { - this(Option(functionNameOverride))(schema, dBEngine) - } - - def this(dBEngine: E) - (implicit schema: DBSchema) = { - this(None)(schema, dBEngine) - } + extends DBFunction[I, R, E, F](functionNameOverride) { /** * For easy and convenient execution of the DB function call @@ -142,7 +95,7 @@ object DBFunction { * @return - a sequence of values, each coming from a row returned from the DB function transformed to scala * type `R` */ - def apply(values: I): Future[Seq[R]] = multipleResults(values) + def apply(values: I): F[Seq[R]] = multipleResults(values) } /** @@ -156,41 +109,16 @@ object DBFunction { * @tparam R - the type covering the returned fields from the database function * @tparam E - the type of the [[DBEngine]] engine */ - abstract class DBSingleResultFunction[I, R, E <: DBEngine](functionNameOverride: Option[String] = None) + abstract class DBSingleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) (implicit schema: DBSchema, dBEngine: E) - extends DBFunction[I, R, E](functionNameOverride) { - - def this(functionNameOverride: String) - (implicit schema: DBSchema, dBEngine: E) = { - this(Option(functionNameOverride))(schema, dBEngine) - } - - def this(schema: DBSchema, functionNameOverride: String) - (implicit dBEngine: E) = { - this(Option(functionNameOverride))(schema, dBEngine) - } - - def this(schema: DBSchema) - (implicit dBEngine: E) = { - this(None)(schema, dBEngine) - } - - def this(dBEngine: E, functionNameOverride: String) - (implicit schema: DBSchema) = { - this(Option(functionNameOverride))(schema, dBEngine) - } - - def this(dBEngine: E) - (implicit schema: DBSchema) = { - this(None)(schema, dBEngine) - } + extends DBFunction[I, R, E, F](functionNameOverride) { /** * For easy and convenient execution of the DB function call * @param values - the values to pass over to the database function * @return - the value returned from the DB function transformed to scala type `R` */ - def apply(values: I): Future[R] = singleResult(values) + def apply(values: I): F[R] = singleResult(values) } /** @@ -204,40 +132,15 @@ object DBFunction { * @tparam R - the type covering the returned fields from the database function * @tparam E - the type of the [[DBEngine]] engine */ - abstract class DBOptionalResultFunction[I, R, E <: DBEngine](functionNameOverride: Option[String] = None) + abstract class DBOptionalResultFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) (implicit schema: DBSchema, dBEngine: E) - extends DBFunction[I, R, E](functionNameOverride) { - - def this(functionNameOverride: String) - (implicit schema: DBSchema, dBEngine: E) = { - this(Option(functionNameOverride))(schema, dBEngine) - } - - def this(schema: DBSchema, functionNameOverride: String) - (implicit dBEngine: E) = { - this(Option(functionNameOverride))(schema, dBEngine) - } - - def this(schema: DBSchema) - (implicit dBEngine: E) = { - this(None)(schema, dBEngine) - } - - def this(dBEngine: E, functionNameOverride: String) - (implicit schema: DBSchema) = { - this(Option(functionNameOverride))(schema, dBEngine) - } - - def this(dBEngine: E) - (implicit schema: DBSchema) = { - this(None)(schema, dBEngine) - } + extends DBFunction[I, R, E, F](functionNameOverride) { /** * For easy and convenient execution of the DB function call * @param values - the values to pass over to the database function * @return - the value returned from the DB function transformed to scala type `R` if a row is returned, otherwise `None` */ - def apply(values: I): Future[Option[R]] = optionalResult(values) + def apply(values: I): F[Option[R]] = optionalResult(values) } } diff --git a/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala b/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala index 47c62436..f1375e31 100644 --- a/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala @@ -1,63 +1,63 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb - -import org.scalatest.funsuite.AnyFunSuite - -import scala.concurrent.{ExecutionContext, Future} -import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention - -class DBFunctionSuite extends AnyFunSuite { - - private def neverHappens: Nothing = { - throw new Exception("Should never get here") - } - - private implicit object EngineThrow extends DBEngine { - override def run[R](query: QueryType[R]): Future[Seq[R]] = neverHappens - - override implicit val executor: ExecutionContext = ExecutionContext.Implicits.global - } - - private object FooNamed extends DBSchema - private object FooNameless extends DBSchema("") - - test("Function name check"){ - case class MyFunction(override val schema: DBSchema) extends DBFunction[Unit, Unit, DBEngine](schema) { - override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens - } - - val fnc1 = MyFunction(FooNamed) - val fnc2 = MyFunction(FooNameless) - - assert(fnc1.functionName == "foo_named.my_function") - assert(fnc2.functionName == "my_function") - } - - test("Function name override check"){ - case class MyFunction(override val schema: DBSchema) extends DBFunction[Unit, Unit, DBEngine](schema, "bar") { - override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens - } - - val fnc1 = MyFunction(FooNamed) - val fnc2 = MyFunction(FooNameless) - - assert(fnc1.functionName == "foo_named.bar") - assert(fnc2.functionName == "bar") - } - -} +///* +// * Copyright 2022 ABSA Group Limited +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package za.co.absa.fadb +// +//import org.scalatest.funsuite.AnyFunSuite +// +//import scala.concurrent.{ExecutionContext, Future} +//import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention +// +//class DBFunctionSuite extends AnyFunSuite { +// +// private def neverHappens: Nothing = { +// throw new Exception("Should never get here") +// } +// +// private implicit object EngineThrow extends DBEngine { +// override def run[R](query: QueryType[R]): Future[Seq[R]] = neverHappens +// +// override implicit val executor: ExecutionContext = ExecutionContext.Implicits.global +// } +// +// private object FooNamed extends DBSchema +// private object FooNameless extends DBSchema("") +// +// test("Function name check"){ +// case class MyFunction(override val schema: DBSchema) extends DBFunction[Unit, Unit, DBEngine](schema) { +// override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens +// } +// +// val fnc1 = MyFunction(FooNamed) +// val fnc2 = MyFunction(FooNameless) +// +// assert(fnc1.functionName == "foo_named.my_function") +// assert(fnc2.functionName == "my_function") +// } +// +// test("Function name override check"){ +// case class MyFunction(override val schema: DBSchema) extends DBFunction[Unit, Unit, DBEngine](schema, "bar") { +// override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens +// } +// +// val fnc1 = MyFunction(FooNamed) +// val fnc2 = MyFunction(FooNameless) +// +// assert(fnc1.functionName == "foo_named.bar") +// assert(fnc2.functionName == "bar") +// } +// +//} diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala new file mode 100644 index 00000000..22a46066 --- /dev/null +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala @@ -0,0 +1,17 @@ +package za.co.absa.fadb.doobie + +import cats.effect.IO +import doobie._ +import doobie.implicits._ +import za.co.absa.fadb.DBEngine + +class DoobiePgEngine(val transactor: Transactor[IO]) extends DBEngine[IO] { + type QueryType[T] = DoobieQuery[T] + + private def run_[R](query: QueryType[R])(implicit readR: Read[R], getR: Get[R]): IO[Seq[R]] = { + query.fragment.query[R].to[Seq].transact(transactor) + } + + override protected def run[R](query: DoobieQuery[R]): IO[Seq[R]] = + run_(query)(query.readR, query.getR) +} diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala new file mode 100644 index 00000000..678d1406 --- /dev/null +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala @@ -0,0 +1,9 @@ +package za.co.absa.fadb.doobie + +import doobie.util.{Get, Read} +import doobie.util.fragment.Fragment +import za.co.absa.fadb.Query + +class DoobieQuery[R: Read](val fragment: Fragment) + (implicit val readR: Read[R], val getR: Get[R]) + extends Query[R] diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunction.scala new file mode 100644 index 00000000..b350f23f --- /dev/null +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunction.scala @@ -0,0 +1,19 @@ +package za.co.absa.fadb.doobie + +import cats.effect.IO +import doobie.util.{Get, Read} +import doobie.util.fragment.Fragment +import za.co.absa.fadb.DBFunction.DBSingleResultFunction +import za.co.absa.fadb.DBSchema + + +trait DoobieFunction[I, R] { + implicit def readR: Read[R] + implicit def getR: Get[R] + def sql(values: I)(implicit read: Read[R], get: Get[R]): Fragment + protected def query(values: I): DoobieQuery[R] = new DoobieQuery[R](sql(values)) +} + +abstract class DoobieSingleResultFunction[I, R, E](implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine) + extends DBSingleResultFunction[I, R, DoobiePgEngine, IO] with DoobieFunction[I, R] { +} diff --git a/doobie/src/test/scala/za/co/absa/fadb/doobie/MyTest.scala b/doobie/src/test/scala/za/co/absa/fadb/doobie/MyTest.scala new file mode 100644 index 00000000..48a27d53 --- /dev/null +++ b/doobie/src/test/scala/za/co/absa/fadb/doobie/MyTest.scala @@ -0,0 +1,54 @@ +package za.co.absa.fadb.doobie + +import cats.effect.IO +import cats.effect.unsafe.implicits.global +import doobie.implicits.toSqlInterpolator +import doobie.util.{Get, Read} +import doobie.util.fragment.Fragment +import doobie.util.transactor.Transactor +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBSchema +import doobie.util.log.{LogEvent, LogHandler} +import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling + +case class CreateActorRequestBody(firstName: String, lastName: String) + +class CreateActor(implicit override val schema: DBSchema, override val dbEngine: DoobiePgEngine) + extends DoobieSingleResultFunction[CreateActorRequestBody, Int, DoobiePgEngine] { + + override implicit val readR: Read[Int] = implicitly[Read[Int]] + override implicit val getR: Get[Int] = implicitly[Get[Int]] + + override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("o_actor_id") + + override def sql(values: CreateActorRequestBody)(implicit read: Read[Int], get: Get[Int]): Fragment = { + sql"SELECT * FROM $functionName('${values.firstName}', '${values.lastName}')" + } +} + +class DoobieTest extends AnyFunSuite { + import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ + object Runs extends DBSchema + + test("DoobieTest") { + val printSqlLogHandler: LogHandler[IO] = new LogHandler[IO] { + def run(logEvent: LogEvent): IO[Unit] = + IO { + println(logEvent.sql) + } + } + + + val transactor = Transactor.fromDriverManager[IO]( + driver = "org.postgresql.ds.PGSimpleDataSource", // JDBC driver classname + url = "jdbc:postgresql://localhost:5432/movies", // Connect URL + user = "postgres", // Database user name + password = "postgres", // Database password + logHandler = Some(printSqlLogHandler) // Don't setup logging for now. See Logging page for how to log events in detail + ) + + val createActor = new CreateActor()(Runs, new DoobiePgEngine(transactor)) + assert(createActor.functionName == "runs.create_actor") + val actorId = createActor(CreateActorRequestBody("John", "Doe")).unsafeRunSync() + } +} diff --git a/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala b/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala index 09b39048..0ae6fc0e 100644 --- a/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala +++ b/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala @@ -1,122 +1,122 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb.examples.enceladus - -import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.slick.{SlickPgEngine, SlickFunction, SlickFunctionWithStatusSupport} -import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention -import slick.jdbc.{GetResult, SQLActionBuilder} -import slick.jdbc.PostgresProfile.api._ - -import java.sql.Timestamp -import scala.concurrent.Future -import DatasetSchema._ -import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBSingleResultFunction} -import za.co.absa.fadb.status.handling.implementations.UserDefinedStatusHandling - -/* The Schema doesn't need the dBEngine directly, but it seems cleaner this way to hand it over to schema's functions */ -class DatasetSchema(implicit engine: SlickPgEngine) extends DBSchema { - - val addSchema = new AddSchema - val getSchema = new GetSchema - val list = new List -} - - -object DatasetSchema { - - case class SchemaInput(schemaName: String, - schemaVersion: Int, - schemaDescription: Option[String], - fields: Option[String], - userName: String) - - case class Schema(idSchemaVersion: Long, - schemaName: String, - schemaVersion: Int, - schemaDescription: Option[String], - fields: Option[String], - createdBy: String, - createdWhen: Timestamp, - updatedBy: String, - updatedWhen: Timestamp, - lockedBy: Option[String], - lockedWhen: Option[Timestamp], - deletedBy: Option[String], - deletedWhen: Option[Timestamp]) - - case class SchemaHeader(entityName: String, entityLatestVersion: Int) - - final class AddSchema(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) - extends DBSingleResultFunction[SchemaInput, Long, SlickPgEngine] - with SlickFunctionWithStatusSupport[SchemaInput, Long] - with UserDefinedStatusHandling { - - override protected def sql(values: SchemaInput): SQLActionBuilder = { - sql"""SELECT A.status, A.status_text, A.id_schema_version - FROM #$functionName(${values.schemaName}, ${values.schemaVersion}, ${values.schemaDescription}, - ${values.fields}::JSONB, ${values.userName} - ) A;""" - } - - override protected def slickConverter: GetResult[Long] = GetResult.GetLong - - override def OKStatuses: Set[Integer] = Set(201) - - } - - final class GetSchema(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) - extends DBSingleResultFunction[(String, Option[Int]), Schema, SlickPgEngine] - with SlickFunctionWithStatusSupport[(String, Option[Int]), Schema] - with UserDefinedStatusHandling { - - /* This is an example of how to deal with overloaded DB functions - see different input type: Long vs what's in the class type: (String, Option[Int]) */ - def apply(id: Long): Future[Schema] = { - val sql = - sql"""SELECT A.* - FROM #$functionName($id) A;""" - - val slickQuery = new dBEngine.QueryType(sql, slickConverter) - dBEngine.fetchHead[Schema](slickQuery) - } - - override protected def sql(values: (String, Option[Int])): SQLActionBuilder = { - sql"""SELECT A.* - FROM #$functionName(${values._1}, ${values._2}) A;""" - } - - override protected val slickConverter: GetResult[Schema] = GetResult{r => - Schema(r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<) - } - - override val OKStatuses: Set[Integer] = Set(200) - } - - final class List(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) - extends DBMultipleResultFunction[Boolean, SchemaHeader, SlickPgEngine]() - with SlickFunction[Boolean, SchemaHeader] { - - override def apply(values: Boolean = false): Future[Seq[SchemaHeader]] = super.apply(values) - - override protected def sql(values: Boolean): SQLActionBuilder = { - sql"""SELECT A.entity_name, A.entity_latest_version - FROM #$functionName($values) as A;""" - } - - override protected val slickConverter: GetResult[SchemaHeader] = GetResult(r => {SchemaHeader(r.<<, r.<<)}) - } -} +///* +// * Copyright 2022 ABSA Group Limited +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package za.co.absa.fadb.examples.enceladus +// +//import za.co.absa.fadb.DBSchema +//import za.co.absa.fadb.slick.{SlickPgEngine, SlickFunction, SlickFunctionWithStatusSupport} +//import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention +//import slick.jdbc.{GetResult, SQLActionBuilder} +//import slick.jdbc.PostgresProfile.api._ +// +//import java.sql.Timestamp +//import scala.concurrent.Future +//import DatasetSchema._ +//import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBSingleResultFunction} +//import za.co.absa.fadb.status.handling.implementations.UserDefinedStatusHandling +// +///* The Schema doesn't need the dBEngine directly, but it seems cleaner this way to hand it over to schema's functions */ +//class DatasetSchema(implicit engine: SlickPgEngine) extends DBSchema { +// +// val addSchema = new AddSchema +// val getSchema = new GetSchema +// val list = new List +//} +// +// +//object DatasetSchema { +// +// case class SchemaInput(schemaName: String, +// schemaVersion: Int, +// schemaDescription: Option[String], +// fields: Option[String], +// userName: String) +// +// case class Schema(idSchemaVersion: Long, +// schemaName: String, +// schemaVersion: Int, +// schemaDescription: Option[String], +// fields: Option[String], +// createdBy: String, +// createdWhen: Timestamp, +// updatedBy: String, +// updatedWhen: Timestamp, +// lockedBy: Option[String], +// lockedWhen: Option[Timestamp], +// deletedBy: Option[String], +// deletedWhen: Option[Timestamp]) +// +// case class SchemaHeader(entityName: String, entityLatestVersion: Int) +// +// final class AddSchema(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) +// extends DBSingleResultFunction[SchemaInput, Long, SlickPgEngine] +// with SlickFunctionWithStatusSupport[SchemaInput, Long] +// with UserDefinedStatusHandling { +// +// override protected def sql(values: SchemaInput): SQLActionBuilder = { +// sql"""SELECT A.status, A.status_text, A.id_schema_version +// FROM #$functionName(${values.schemaName}, ${values.schemaVersion}, ${values.schemaDescription}, +// ${values.fields}::JSONB, ${values.userName} +// ) A;""" +// } +// +// override protected def slickConverter: GetResult[Long] = GetResult.GetLong +// +// override def OKStatuses: Set[Integer] = Set(201) +// +// } +// +// final class GetSchema(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) +// extends DBSingleResultFunction[(String, Option[Int]), Schema, SlickPgEngine] +// with SlickFunctionWithStatusSupport[(String, Option[Int]), Schema] +// with UserDefinedStatusHandling { +// +// /* This is an example of how to deal with overloaded DB functions - see different input type: Long vs what's in the class type: (String, Option[Int]) */ +// def apply(id: Long): Future[Schema] = { +// val sql = +// sql"""SELECT A.* +// FROM #$functionName($id) A;""" +// +// val slickQuery = new dBEngine.QueryType(sql, slickConverter) +// dBEngine.fetchHead[Schema](slickQuery) +// } +// +// override protected def sql(values: (String, Option[Int])): SQLActionBuilder = { +// sql"""SELECT A.* +// FROM #$functionName(${values._1}, ${values._2}) A;""" +// } +// +// override protected val slickConverter: GetResult[Schema] = GetResult{r => +// Schema(r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<) +// } +// +// override val OKStatuses: Set[Integer] = Set(200) +// } +// +// final class List(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) +// extends DBMultipleResultFunction[Boolean, SchemaHeader, SlickPgEngine]() +// with SlickFunction[Boolean, SchemaHeader] { +// +// override def apply(values: Boolean = false): Future[Seq[SchemaHeader]] = super.apply(values) +// +// override protected def sql(values: Boolean): SQLActionBuilder = { +// sql"""SELECT A.entity_name, A.entity_latest_version +// FROM #$functionName($values) as A;""" +// } +// +// override protected val slickConverter: GetResult[SchemaHeader] = GetResult(r => {SchemaHeader(r.<<, r.<<)}) +// } +//} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index ced14ddf..51495066 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -18,8 +18,8 @@ import sbt._ object Dependencies { - private def commonDependencies(scalaVersion: String): Seq[ModuleID] = Seq( + "org.typelevel" %% "cats-core" % "2.9.0", "org.scalatest" %% "scalatest" % "3.1.0" % "test,it", "org.scalatest" %% "scalatest-flatspec" % "3.2.0" % "test,it", "org.scalatestplus" %% "mockito-1-10" % "3.1.0.0" % "test,it" @@ -42,6 +42,15 @@ object Dependencies { ) } + def doobieDependencies(scalaVersion: String): Seq[ModuleID] = { + commonDependencies(scalaVersion) ++ Seq( +// "org.typelevel" %% "cats-effect" % "3.5.2", + "org.tpolecat" %% "doobie-core" % "1.0.0-RC4", + "org.tpolecat" %% "doobie-hikari" % "1.0.0-RC4", + "org.tpolecat" %% "doobie-postgres" % "1.0.0-RC4" + ) + } + def examplesDependencies(scalaVersion: String): Seq[ModuleID] = { slickDependencies(scalaVersion) } diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala index 78a2776e..4d7f8e0d 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala @@ -28,7 +28,7 @@ import scala.language.higherKinds * [[DBEngine]] based on the Slick library in the Postgres flavor * @param db - the Slick database */ -class SlickPgEngine(val db: Database)(implicit val executor: ExecutionContext) extends DBEngine { +class SlickPgEngine(val db: Database)(implicit val executor: ExecutionContext) extends DBEngine[Future] { /** * The type of Queries for Slick From 264f510638645b456e5a5b45c76a9ed098782b71 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Mon, 4 Dec 2023 00:11:59 +0100 Subject: [PATCH 04/90] tmp code of doobie integration --- .../co/absa/fadb/doobie/DoobieFunction.scala | 58 +++++++++++++++ .../co/absa/fadb/doobie/DoobiePgEngine.scala | 6 +- .../za/co/absa/fadb/doobie/DoobieQuery.scala | 2 +- .../doobie/DoobieSingleResultFunction.scala | 19 ----- .../scala/za/co/absa/fadb/doobie/MyTest.scala | 72 ++++++++++++------- 5 files changed, 109 insertions(+), 48 deletions(-) create mode 100644 doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala delete mode 100644 doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunction.scala diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala new file mode 100644 index 00000000..2291e66b --- /dev/null +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -0,0 +1,58 @@ +package za.co.absa.fadb.doobie + +import cats.effect.IO +import doobie.util.Read +import doobie.util.fragment.Fragment +import za.co.absa.fadb.DBFunction._ +import za.co.absa.fadb.status.FunctionStatus +import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling +import za.co.absa.fadb.{DBFunctionFabric, DBSchema} + +import scala.util.{Failure, Success} + + +trait DoobieFunction[I, R] extends DBFunctionFabric { + implicit val readR: Read[R] + def sql(values: I)(implicit read: Read[R]): Fragment + protected def query(values: I): DoobieQuery[R] = new DoobieQuery[R](sql(values)) +} + +trait DoobieFunctionWithStatusSupport[I, R] extends DBFunctionFabric with StandardStatusHandling { + implicit val readR: Read[R] + implicit val readSelectWithStatus: Read[(Int, String, R)] + def sql(values: I)(implicit read: Read[R]): Fragment + protected def query(values: I): DoobieQuery[(Int, String, R)] = new DoobieQuery[(Int, String, R)](sql(values)) +} + +object DoobieFunction { + abstract class DoobieSingleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine, val readR: Read[R]) + extends DBSingleResultFunction[I, R, DoobiePgEngine, IO](functionNameOverride) with DoobieFunction[I, R] { + } + + abstract class DoobieMultipleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine, val readR: Read[R]) + extends DBMultipleResultFunction[I, R, DoobiePgEngine, IO](functionNameOverride) with DoobieFunction[I, R] { + } + + abstract class DoobieOptionalResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine, val readR: Read[R]) + extends DBOptionalResultFunction[I, R, DoobiePgEngine, IO](functionNameOverride) with DoobieFunction[I, R] { + } + + case class SuccessfulResult[R](functionStatus: FunctionStatus, result: R) + case class FailedResult(functionStatus: FunctionStatus, failure: Throwable) + + type OutcomeWithStatus[R] = Either[FailedResult, SuccessfulResult[R]] + + abstract class DoobieSingleResultFunctionWithStatusSupport[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine, val readR: Read[R], val readSelectWithStatus: Read[(Int, String, R)]) + extends DBSingleResultFunction[I, (Int, String, R), DoobiePgEngine, IO](functionNameOverride) with DoobieFunctionWithStatusSupport[I, R] { + + def applyWithStatusHandling(values: I): IO[OutcomeWithStatus[R]] = { + super.apply(values).flatMap { + case (status, statusText, result) => + checkStatus(status, statusText) match { + case Success(_) => IO.pure(Right(SuccessfulResult[R](FunctionStatus(status, statusText), result))) + case Failure(e) => IO.pure(Left(FailedResult(FunctionStatus(status, statusText), e))) + } + } + } + } +} diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala index 22a46066..9574580d 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala @@ -8,10 +8,10 @@ import za.co.absa.fadb.DBEngine class DoobiePgEngine(val transactor: Transactor[IO]) extends DBEngine[IO] { type QueryType[T] = DoobieQuery[T] - private def run_[R](query: QueryType[R])(implicit readR: Read[R], getR: Get[R]): IO[Seq[R]] = { + private def run_[R](query: QueryType[R])(implicit readR: Read[R]): IO[Seq[R]] = { query.fragment.query[R].to[Seq].transact(transactor) } - override protected def run[R](query: DoobieQuery[R]): IO[Seq[R]] = - run_(query)(query.readR, query.getR) + override def run[R](query: DoobieQuery[R]): IO[Seq[R]] = + run_(query)(query.readR) } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala index 678d1406..4e82d68a 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala @@ -5,5 +5,5 @@ import doobie.util.fragment.Fragment import za.co.absa.fadb.Query class DoobieQuery[R: Read](val fragment: Fragment) - (implicit val readR: Read[R], val getR: Get[R]) + (implicit val readR: Read[R]) // , val getR: Get[R] extends Query[R] diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunction.scala deleted file mode 100644 index b350f23f..00000000 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunction.scala +++ /dev/null @@ -1,19 +0,0 @@ -package za.co.absa.fadb.doobie - -import cats.effect.IO -import doobie.util.{Get, Read} -import doobie.util.fragment.Fragment -import za.co.absa.fadb.DBFunction.DBSingleResultFunction -import za.co.absa.fadb.DBSchema - - -trait DoobieFunction[I, R] { - implicit def readR: Read[R] - implicit def getR: Get[R] - def sql(values: I)(implicit read: Read[R], get: Get[R]): Fragment - protected def query(values: I): DoobieQuery[R] = new DoobieQuery[R](sql(values)) -} - -abstract class DoobieSingleResultFunction[I, R, E](implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine) - extends DBSingleResultFunction[I, R, DoobiePgEngine, IO] with DoobieFunction[I, R] { -} diff --git a/doobie/src/test/scala/za/co/absa/fadb/doobie/MyTest.scala b/doobie/src/test/scala/za/co/absa/fadb/doobie/MyTest.scala index 48a27d53..2cd314cc 100644 --- a/doobie/src/test/scala/za/co/absa/fadb/doobie/MyTest.scala +++ b/doobie/src/test/scala/za/co/absa/fadb/doobie/MyTest.scala @@ -3,52 +3,74 @@ package za.co.absa.fadb.doobie import cats.effect.IO import cats.effect.unsafe.implicits.global import doobie.implicits.toSqlInterpolator -import doobie.util.{Get, Read} +import doobie.util.Read +import doobie.util.Read._ import doobie.util.fragment.Fragment +import doobie.util.log.{LogEvent, LogHandler} import doobie.util.transactor.Transactor import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import doobie.util.log.{LogEvent, LogHandler} -import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling +import za.co.absa.fadb.doobie.DoobieFunction.{DoobieSingleResultFunction, DoobieSingleResultFunctionWithStatusSupport} +import za.co.absa.fadb.status.FunctionStatus case class CreateActorRequestBody(firstName: String, lastName: String) +case class IntWithStatusAndStatusText(status: Int, status_text: String, o_actor_id: Int) class CreateActor(implicit override val schema: DBSchema, override val dbEngine: DoobiePgEngine) - extends DoobieSingleResultFunction[CreateActorRequestBody, Int, DoobiePgEngine] { + extends DoobieSingleResultFunction[CreateActorRequestBody, IntWithStatusAndStatusText] { - override implicit val readR: Read[Int] = implicitly[Read[Int]] - override implicit val getR: Get[Int] = implicitly[Get[Int]] + override implicit val readR: Read[IntWithStatusAndStatusText] = Read[(Int, String, Int)].map { + case (status, statusText, actorId) => IntWithStatusAndStatusText(status, statusText, actorId) + } - override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("o_actor_id") + // override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("o_actor_id") - override def sql(values: CreateActorRequestBody)(implicit read: Read[Int], get: Get[Int]): Fragment = { - sql"SELECT * FROM $functionName('${values.firstName}', '${values.lastName}')" + override def sql(values: CreateActorRequestBody)(implicit read: Read[IntWithStatusAndStatusText]): Fragment = { + sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" } } +class CreateActorWithStatusHandling(functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, override val dbEngine: DoobiePgEngine) + extends DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int](functionNameOverride) { + + override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = + sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" +} + class DoobieTest extends AnyFunSuite { import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ object Runs extends DBSchema - test("DoobieTest") { - val printSqlLogHandler: LogHandler[IO] = new LogHandler[IO] { - def run(logEvent: LogEvent): IO[Unit] = - IO { - println(logEvent.sql) - } - } - + val printSqlLogHandler: LogHandler[IO] = new LogHandler[IO] { + def run(logEvent: LogEvent): IO[Unit] = + IO { + println(logEvent.sql) + } + } - val transactor = Transactor.fromDriverManager[IO]( - driver = "org.postgresql.ds.PGSimpleDataSource", // JDBC driver classname - url = "jdbc:postgresql://localhost:5432/movies", // Connect URL - user = "postgres", // Database user name - password = "postgres", // Database password - logHandler = Some(printSqlLogHandler) // Don't setup logging for now. See Logging page for how to log events in detail - ) + val transactor = Transactor.fromDriverManager[IO]( + driver = "org.postgresql.ds.PGSimpleDataSource", + url = "jdbc:postgresql://localhost:5432/movies", + user = "postgres", + password = "postgres", + logHandler = Some(printSqlLogHandler) + ) + test("DoobieTest") { val createActor = new CreateActor()(Runs, new DoobiePgEngine(transactor)) assert(createActor.functionName == "runs.create_actor") - val actorId = createActor(CreateActorRequestBody("John", "Doe")).unsafeRunSync() + val actorId = createActor(CreateActorRequestBody("Dominika", "Salamonova")).unsafeRunSync() + assert(actorId.status == 11) + assert(actorId.status_text == "Actor created") + } + + test("DoobieTest with status handling") { + val createActorWithStatusHandling = new CreateActorWithStatusHandling(Some("create_actor"))(Runs, new DoobiePgEngine(transactor)) + createActorWithStatusHandling.applyWithStatusHandling(CreateActorRequestBody("Marek", "Salamon")).unsafeRunSync() match { + case Right(success) => + assert(success.functionStatus == FunctionStatus(11, "Actor created")) + case Left(failure) => + fail(failure.failure) + } } } From 27fac5252d800c5264034bb91218450a6e9cf6b1 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Mon, 4 Dec 2023 08:42:21 +0100 Subject: [PATCH 05/90] tmp code of doobie integration --- .scalafmt.conf | 32 +++ build.sbt | 2 +- .../scala/za/co/absa/fadb/DBFunction.scala | 3 - .../za/co/absa/fadb/DBFunctionSuite.scala | 132 ++++++----- .../co/absa/fadb/doobie/DoobieFunction.scala | 215 ++++++++++++++++-- .../co/absa/fadb/doobie/DoobiePgEngine.scala | 27 ++- .../za/co/absa/fadb/doobie/DoobieQuery.scala | 13 +- .../za/co/absa/fadb/doobie/Results.scala | 28 +++ .../scala/za/co/absa/fadb/doobie/MyTest.scala | 76 ------- project/Dependencies.scala | 1 - 10 files changed, 353 insertions(+), 176 deletions(-) create mode 100644 .scalafmt.conf create mode 100644 doobie/src/main/scala/za/co/absa/fadb/doobie/Results.scala delete mode 100644 doobie/src/test/scala/za/co/absa/fadb/doobie/MyTest.scala diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 00000000..d174fd45 --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,32 @@ +version = "3.5.3" +runner.dialect = scala212 + +maxColumn = 120 + +align.preset = some +align.multiline = false + +align.tokens = [ + { + code = "<-" + }, + { + code = "=>" + owners = [{ + regex = "Case" + }] + } +] + +indent.main = 2 +indent.defnSite = 2 + +lineEndings = unix + +docstrings.blankFirstLine = yes +docstrings.style = AsteriskSpace +docstrings.wrap = no +docstrings.removeEmpty = true + +align.openParenDefnSite = false +align.openParenCallSite = false \ No newline at end of file diff --git a/build.sbt b/build.sbt index 9de2063d..d69203c7 100644 --- a/build.sbt +++ b/build.sbt @@ -49,7 +49,7 @@ lazy val commonJacocoExcludes: Seq[String] = Seq( ) lazy val parent = (project in file(".")) - .aggregate(faDbCore, faDBSlick, faDBExamples) + .aggregate(faDbCore, faDBSlick, faDBDoobie, faDBExamples) .settings( name := "root", libraryDependencies ++= rootDependencies(scalaVersion.value), diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 04763864..b0d6399a 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -35,9 +35,6 @@ import scala.language.higherKinds abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) (implicit val schema: DBSchema, val dBEngine: E) extends DBFunctionFabric { - /* alternative constructors for different availability of input parameters */ - - /** * Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the * DBEngine specific mix-in. diff --git a/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala b/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala index f1375e31..51fcb04a 100644 --- a/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala @@ -1,63 +1,69 @@ -///* -// * Copyright 2022 ABSA Group Limited -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -// -//package za.co.absa.fadb -// -//import org.scalatest.funsuite.AnyFunSuite -// -//import scala.concurrent.{ExecutionContext, Future} -//import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention -// -//class DBFunctionSuite extends AnyFunSuite { -// -// private def neverHappens: Nothing = { -// throw new Exception("Should never get here") -// } -// -// private implicit object EngineThrow extends DBEngine { -// override def run[R](query: QueryType[R]): Future[Seq[R]] = neverHappens -// -// override implicit val executor: ExecutionContext = ExecutionContext.Implicits.global -// } -// -// private object FooNamed extends DBSchema -// private object FooNameless extends DBSchema("") -// -// test("Function name check"){ -// case class MyFunction(override val schema: DBSchema) extends DBFunction[Unit, Unit, DBEngine](schema) { -// override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens -// } -// -// val fnc1 = MyFunction(FooNamed) -// val fnc2 = MyFunction(FooNameless) -// -// assert(fnc1.functionName == "foo_named.my_function") -// assert(fnc2.functionName == "my_function") -// } -// -// test("Function name override check"){ -// case class MyFunction(override val schema: DBSchema) extends DBFunction[Unit, Unit, DBEngine](schema, "bar") { -// override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens -// } -// -// val fnc1 = MyFunction(FooNamed) -// val fnc2 = MyFunction(FooNameless) -// -// assert(fnc1.functionName == "foo_named.bar") -// assert(fnc2.functionName == "bar") -// } -// -//} +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.fadb + +import cats.implicits._ +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBFunction.DBSingleResultFunction +import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future + +class DBFunctionSuite extends AnyFunSuite { + + private def neverHappens: Nothing = { + throw new Exception("Should never get here") + } + + class EngineThrow extends DBEngine[Future] { + override def run[R](query: QueryType[R]): Future[Seq[R]] = neverHappens + } + + private object FooNamed extends DBSchema + private object FooNameless extends DBSchema("") + + test("Function name check"){ + + class MyFunction(functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, val dbEngine: EngineThrow) + extends DBSingleResultFunction[Unit, Unit, EngineThrow, Future](None) { + + override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens + } + + val fnc1 = new MyFunction()(FooNamed, new EngineThrow) + val fnc2 = new MyFunction()(FooNameless, new EngineThrow) + + assert(fnc1.functionName == "foo_named.my_function") + assert(fnc2.functionName == "my_function") + } + + test("Function name override check"){ + class MyFunction(implicit override val schema: DBSchema, val dbEngine: EngineThrow) + extends DBSingleResultFunction[Unit, Unit, EngineThrow, Future](Some("bar")) { + + override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens + } + + val fnc1 = new MyFunction()(FooNamed, new EngineThrow) + val fnc2 = new MyFunction()(FooNameless, new EngineThrow) + + assert(fnc1.functionName == "foo_named.bar") + assert(fnc2.functionName == "bar") + } + +} diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index 2291e66b..eb4450b6 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -4,55 +4,220 @@ import cats.effect.IO import doobie.util.Read import doobie.util.fragment.Fragment import za.co.absa.fadb.DBFunction._ +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.doobie.Results.{FailedResult, ResultWithStatus, SuccessfulResult} import za.co.absa.fadb.status.FunctionStatus import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling -import za.co.absa.fadb.{DBFunctionFabric, DBSchema} import scala.util.{Failure, Success} +/** + * `DoobieFunction` provides support for executing database functions using Doobie. + * + * @tparam I the input type of the function + * @tparam R the result type of the function + */ +private [doobie] trait DoobieFunction[I, R] { -trait DoobieFunction[I, R] extends DBFunctionFabric { + /** + * The `Read[R]` instance used to read the query result into `R`. + */ implicit val readR: Read[R] + + /** + * Generates a Doobie `Fragment` representing the SQL query for the function. + * + * @param values the input values for the function + * @return the Doobie `Fragment` representing the SQL query + */ def sql(values: I)(implicit read: Read[R]): Fragment + + /** + * Generates a `DoobieQuery[R]` representing the SQL query for the function. + * + * @param values the input values for the function + * @return the `DoobieQuery[R]` representing the SQL query + */ protected def query(values: I): DoobieQuery[R] = new DoobieQuery[R](sql(values)) } -trait DoobieFunctionWithStatusSupport[I, R] extends DBFunctionFabric with StandardStatusHandling { +/** + * `DoobieFunctionWithStatusSupport` provides support for executing database functions with status handling using Doobie. + * + * @tparam I the input type of the function + * @tparam R the result type of the function + */ +private [doobie] trait DoobieFunctionWithStatusSupport[I, R] extends StandardStatusHandling { + + /** + * The `Read[R]` instance used to read the query result into `R`. + */ implicit val readR: Read[R] + + /** + * The `Read[(Int, String, R)]` instance used to read the query result with status into `(Int, String, R)`. + */ implicit val readSelectWithStatus: Read[(Int, String, R)] + + /** + * Generates a Doobie `Fragment` representing the SQL query for the function. + * + * @param values the input values for the function + * @return the Doobie `Fragment` representing the SQL query + */ def sql(values: I)(implicit read: Read[R]): Fragment + + /** + * Generates a `DoobieQuery[(Int, String, R)]` representing the SQL query for the function with status. + * + * @param values the input values for the function + * @return the `DoobieQuery[(Int, String, R)]` representing the SQL query with status + */ protected def query(values: I): DoobieQuery[(Int, String, R)] = new DoobieQuery[(Int, String, R)](sql(values)) } +/** + * `DoobieFunction` is an object that contains several abstract classes extending different types of database functions. + * These classes use Doobie's `Fragment` to represent SQL queries and `DoobiePgEngine` to execute them. + */ object DoobieFunction { - abstract class DoobieSingleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine, val readR: Read[R]) - extends DBSingleResultFunction[I, R, DoobiePgEngine, IO](functionNameOverride) with DoobieFunction[I, R] { - } - abstract class DoobieMultipleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine, val readR: Read[R]) - extends DBMultipleResultFunction[I, R, DoobiePgEngine, IO](functionNameOverride) with DoobieFunction[I, R] { - } + /** + * `DoobieSingleResultFunction` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type and `IO` as the effect type. + * It represents a database function that returns a single result. + * + * @param functionNameOverride the optional override for the function name + * @param schema the database schema + * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param readR the `Read[R]` instance used to read the query result into `R` + */ + abstract class DoobieSingleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit + override val schema: DBSchema, + val dbEngine: DoobiePgEngine, + val readR: Read[R] + ) extends DBSingleResultFunction[I, R, DoobiePgEngine, IO](functionNameOverride) + with DoobieFunction[I, R] {} - abstract class DoobieOptionalResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine, val readR: Read[R]) - extends DBOptionalResultFunction[I, R, DoobiePgEngine, IO](functionNameOverride) with DoobieFunction[I, R] { - } + /** + * `DoobieMultipleResultFunction` is an abstract class that extends `DBMultipleResultFunction` with `DoobiePgEngine` as the engine type and `IO` as the effect type. + * It represents a database function that returns multiple results. + * + * @param functionNameOverride the optional override for the function name + * @param schema the database schema + * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param readR the `Read[R]` instance used to read the query result into `R` + */ + abstract class DoobieMultipleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit + override val schema: DBSchema, + val dbEngine: DoobiePgEngine, + val readR: Read[R] + ) extends DBMultipleResultFunction[I, R, DoobiePgEngine, IO](functionNameOverride) + with DoobieFunction[I, R] {} - case class SuccessfulResult[R](functionStatus: FunctionStatus, result: R) - case class FailedResult(functionStatus: FunctionStatus, failure: Throwable) + /** + * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` with `DoobiePgEngine` as the engine type and `IO` as the effect type. + * It represents a database function that returns an optional result. + * + * @param functionNameOverride the optional override for the function name + * @param schema the database schema + * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param readR the `Read[R]` instance used to read the query result into `R` + */ + abstract class DoobieOptionalResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit + override val schema: DBSchema, + val dbEngine: DoobiePgEngine, + val readR: Read[R] + ) extends DBOptionalResultFunction[I, R, DoobiePgEngine, IO](functionNameOverride) + with DoobieFunction[I, R] {} - type OutcomeWithStatus[R] = Either[FailedResult, SuccessfulResult[R]] + /** + * `DoobieSingleResultFunctionWithStatusSupport` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type and `IO` as the effect type. + * It represents a database function that returns a single result and supports function status. + * + * @param functionNameOverride the optional override for the function name + * @param schema the database schema + * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param readR the `Read[R]` instance used to read the query result into `R` + * @param readSelectWithStatus the `Read[(Int, String, R)]` instance used to read the query result with status into `(Int, String, R)` + */ + abstract class DoobieSingleResultFunctionWithStatusSupport[I, R](functionNameOverride: Option[String] = None)(implicit + override val schema: DBSchema, + val dbEngine: DoobiePgEngine, + val readR: Read[R], + val readSelectWithStatus: Read[(Int, String, R)] + ) extends DBSingleResultFunction[I, (Int, String, R), DoobiePgEngine, IO](functionNameOverride) + with DoobieFunctionWithStatusSupport[I, R] { - abstract class DoobieSingleResultFunctionWithStatusSupport[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine, val readR: Read[R], val readSelectWithStatus: Read[(Int, String, R)]) - extends DBSingleResultFunction[I, (Int, String, R), DoobiePgEngine, IO](functionNameOverride) with DoobieFunctionWithStatusSupport[I, R] { - - def applyWithStatusHandling(values: I): IO[OutcomeWithStatus[R]] = { - super.apply(values).flatMap { - case (status, statusText, result) => - checkStatus(status, statusText) match { - case Success(_) => IO.pure(Right(SuccessfulResult[R](FunctionStatus(status, statusText), result))) - case Failure(e) => IO.pure(Left(FailedResult(FunctionStatus(status, statusText), e))) - } + /** + * Executes the function with the given input values and returns the result with status. + * + * @param values the input values for the function + * @return the result with status + */ + def applyWithStatus(values: I): IO[ResultWithStatus[R]] = { + super.apply(values).flatMap { case (status, statusText, result) => + checkStatus(status, statusText) match { + case Success(_) => IO.pure(Right(SuccessfulResult[R](FunctionStatus(status, statusText), result))) + case Failure(e) => IO.pure(Left(FailedResult(FunctionStatus(status, statusText), e))) + } } } } + // ==Example== + // {{{ + // CREATE OR REPLACE FUNCTION runs.create_actor( + // IN i_first_name TEXT, + // IN i_last_name TEXT, + // OUT status INTEGER, + // OUT status_text TEXT, + // OUT o_actor_id INTEGER + // ) RETURNS record AS + // $$ + // BEGIN + // INSERT INTO runs.actors(first_name, last_name) + // VALUES (i_first_name, i_last_name) + // RETURNING actor_id INTO o_actor_id; + // + // status := 11; + // status_text := 'Actor created'; + // + // RETURN; + // END; + // $$ + // LANGUAGE plpgsql; + // + // case class CreateActorRequestBody(firstName: String, lastName: String) + // + // class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine) + // extends DoobieFunction.DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int] { + // + // override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): fragment.Fragment = + // sql"SELECT * FROM create_actor(${values.firstName}, ${values.lastName})" + // } + // + // class DoobieTest extends AnyFunSuite { + // import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ + // object Runs extends DBSchema + // + // private val transactor = Transactor.fromDriverManager[IO]( + // "org.postgresql.ds.PGSimpleDataSource", + // "jdbc:postgresql://localhost:5432/movies", + // "postgres", + // "postgres" + // ) + // + // private val createActor = new CreateActor()(Runs, new DoobiePgEngine(transactor)) + // + // test("DoobieTest with status handling") { + // val requestBody = CreateActorRequestBody("Pavel", "Marek") + // createActor.applyWithStatus(requestBody).unsafeRunSync() match { + // case Right(success) => + // assert(success.functionStatus == status.FunctionStatus(11, "Actor created")) + // case Left(failure) => + // fail(failure.failure) + // } + // } + // } + // }}} + } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala index 9574580d..29603df8 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala @@ -5,13 +5,34 @@ import doobie._ import doobie.implicits._ import za.co.absa.fadb.DBEngine +/** + * `DoobiePgEngine` is a class that extends `DBEngine` with `IO` as the effect type. + * It uses Doobie's `Transactor[IO]` to execute SQL queries. + * + * @param transactor the Doobie transactor for executing SQL queries + */ class DoobiePgEngine(val transactor: Transactor[IO]) extends DBEngine[IO] { + + /** The type of Doobie queries that produce `T` */ type QueryType[T] = DoobieQuery[T] - private def run_[R](query: QueryType[R])(implicit readR: Read[R]): IO[Seq[R]] = { + /** + * Executes a Doobie query and returns the result as an `IO[Seq[R]]`. + * + * @param query the Doobie query to execute + * @param readR the `Read[R]` instance used to read the query result into `R` + * @return the query result as an `IO[Seq[R]]` + */ + private def executeQuery[R](query: QueryType[R])(implicit readR: Read[R]): IO[Seq[R]] = { query.fragment.query[R].to[Seq].transact(transactor) } - override def run[R](query: DoobieQuery[R]): IO[Seq[R]] = - run_(query)(query.readR) + /** + * Runs a Doobie query and returns the result as an `IO[Seq[R]]`. + * + * @param query the Doobie query to run + * @return the query result as an `IO[Seq[R]]` + */ + override def run[R](query: QueryType[R]): IO[Seq[R]] = + executeQuery(query)(query.readR) } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala index 4e82d68a..a152c137 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala @@ -1,9 +1,14 @@ package za.co.absa.fadb.doobie -import doobie.util.{Get, Read} +import doobie.util.Read import doobie.util.fragment.Fragment import za.co.absa.fadb.Query -class DoobieQuery[R: Read](val fragment: Fragment) - (implicit val readR: Read[R]) // , val getR: Get[R] - extends Query[R] +/** + * `DoobieQuery` is a class that extends `Query` with `R` as the result type. + * It uses Doobie's `Fragment` to represent SQL queries. + * + * @param fragment the Doobie fragment representing the SQL query + * @param readR the `Read[R]` instance used to read the query result into `R` + */ +class DoobieQuery[R: Read](val fragment: Fragment)(implicit val readR: Read[R]) extends Query[R] diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/Results.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/Results.scala new file mode 100644 index 00000000..760c5e0d --- /dev/null +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/Results.scala @@ -0,0 +1,28 @@ +package za.co.absa.fadb.doobie + +import za.co.absa.fadb.status.FunctionStatus + +object Results { + + /** + * `SuccessfulResult` is a case class that represents a successful result of a function. + * + * @param functionStatus the status of the function + * @param result the result of the function + */ + case class SuccessfulResult[R](functionStatus: FunctionStatus, result: R) + + /** + * `FailedResult` is a case class that represents a failed result of a function. + * + * @param functionStatus the status of the function + * @param failure the exception that caused the function to fail + */ + case class FailedResult(functionStatus: FunctionStatus, failure: Throwable) + + /** + * `ResultWithStatus` is a type alias for `Either[FailedResult, SuccessfulResult[R]]`. + * It represents a result of a function that can either be successful or failed. + */ + type ResultWithStatus[R] = Either[FailedResult, SuccessfulResult[R]] +} diff --git a/doobie/src/test/scala/za/co/absa/fadb/doobie/MyTest.scala b/doobie/src/test/scala/za/co/absa/fadb/doobie/MyTest.scala deleted file mode 100644 index 2cd314cc..00000000 --- a/doobie/src/test/scala/za/co/absa/fadb/doobie/MyTest.scala +++ /dev/null @@ -1,76 +0,0 @@ -package za.co.absa.fadb.doobie - -import cats.effect.IO -import cats.effect.unsafe.implicits.global -import doobie.implicits.toSqlInterpolator -import doobie.util.Read -import doobie.util.Read._ -import doobie.util.fragment.Fragment -import doobie.util.log.{LogEvent, LogHandler} -import doobie.util.transactor.Transactor -import org.scalatest.funsuite.AnyFunSuite -import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobie.DoobieFunction.{DoobieSingleResultFunction, DoobieSingleResultFunctionWithStatusSupport} -import za.co.absa.fadb.status.FunctionStatus - -case class CreateActorRequestBody(firstName: String, lastName: String) -case class IntWithStatusAndStatusText(status: Int, status_text: String, o_actor_id: Int) - -class CreateActor(implicit override val schema: DBSchema, override val dbEngine: DoobiePgEngine) - extends DoobieSingleResultFunction[CreateActorRequestBody, IntWithStatusAndStatusText] { - - override implicit val readR: Read[IntWithStatusAndStatusText] = Read[(Int, String, Int)].map { - case (status, statusText, actorId) => IntWithStatusAndStatusText(status, statusText, actorId) - } - - // override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("o_actor_id") - - override def sql(values: CreateActorRequestBody)(implicit read: Read[IntWithStatusAndStatusText]): Fragment = { - sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" - } -} - -class CreateActorWithStatusHandling(functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, override val dbEngine: DoobiePgEngine) - extends DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int](functionNameOverride) { - - override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = - sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" -} - -class DoobieTest extends AnyFunSuite { - import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ - object Runs extends DBSchema - - val printSqlLogHandler: LogHandler[IO] = new LogHandler[IO] { - def run(logEvent: LogEvent): IO[Unit] = - IO { - println(logEvent.sql) - } - } - - val transactor = Transactor.fromDriverManager[IO]( - driver = "org.postgresql.ds.PGSimpleDataSource", - url = "jdbc:postgresql://localhost:5432/movies", - user = "postgres", - password = "postgres", - logHandler = Some(printSqlLogHandler) - ) - - test("DoobieTest") { - val createActor = new CreateActor()(Runs, new DoobiePgEngine(transactor)) - assert(createActor.functionName == "runs.create_actor") - val actorId = createActor(CreateActorRequestBody("Dominika", "Salamonova")).unsafeRunSync() - assert(actorId.status == 11) - assert(actorId.status_text == "Actor created") - } - - test("DoobieTest with status handling") { - val createActorWithStatusHandling = new CreateActorWithStatusHandling(Some("create_actor"))(Runs, new DoobiePgEngine(transactor)) - createActorWithStatusHandling.applyWithStatusHandling(CreateActorRequestBody("Marek", "Salamon")).unsafeRunSync() match { - case Right(success) => - assert(success.functionStatus == FunctionStatus(11, "Actor created")) - case Left(failure) => - fail(failure.failure) - } - } -} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 51495066..76b53f07 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -44,7 +44,6 @@ object Dependencies { def doobieDependencies(scalaVersion: String): Seq[ModuleID] = { commonDependencies(scalaVersion) ++ Seq( -// "org.typelevel" %% "cats-effect" % "3.5.2", "org.tpolecat" %% "doobie-core" % "1.0.0-RC4", "org.tpolecat" %% "doobie-hikari" % "1.0.0-RC4", "org.tpolecat" %% "doobie-postgres" % "1.0.0-RC4" From c1baa582ceaccd3f84f042b162cbb8c547872275 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Mon, 4 Dec 2023 12:26:32 +0100 Subject: [PATCH 06/90] it example, status handling (extends not the implementation) --- .../DoobieFunctionWithStatusSupportTest.scala | 58 ++++++++++++++++++ .../co/absa/fadb/doobie/DoobieFunction.scala | 61 ++----------------- .../fadb/slick/FaDbPostgresProfileSuite.scala | 5 +- 3 files changed, 65 insertions(+), 59 deletions(-) create mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusSupportTest.scala diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusSupportTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusSupportTest.scala new file mode 100644 index 00000000..cdb3eb64 --- /dev/null +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusSupportTest.scala @@ -0,0 +1,58 @@ +package za.co.absa.fadb.doobie + +import cats.effect.IO +import cats.effect.unsafe.implicits.global +import doobie.implicits.toSqlInterpolator +import doobie.util.Read +import doobie.util.log.{LogEvent, LogHandler} +import doobie.{Fragment, Transactor} +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStatusSupport +import za.co.absa.fadb.status.FunctionStatus +import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling + +class DoobieFunctionWithStatusSupportTest extends AnyFunSuite { + + case class CreateActorRequestBody(firstName: String, lastName: String) + + class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine) + extends DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int] + with StandardStatusHandling { + + override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = + sql"SELECT * FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" + } + + import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ + object Runs extends DBSchema + + val printSqlLogHandler: LogHandler[IO] = new LogHandler[IO] { + def run(logEvent: LogEvent): IO[Unit] = + IO { + println(logEvent.sql) + } + } + + private val transactor = Transactor.fromDriverManager[IO]( + "org.postgresql.ds.PGSimpleDataSource", + "jdbc:postgresql://localhost:5432/movies", + "postgres", + "postgres", + Some(printSqlLogHandler) + ) + + private val createActor = new CreateActor()(Runs, new DoobiePgEngine(transactor)) + + test("DoobieTest with status handling") { + val requestBody = CreateActorRequestBody("Pavel", "Marek") + createActor.applyWithStatus(requestBody).unsafeRunSync() match { + case Right(success) => + assert(success.functionStatus == FunctionStatus(11, "Actor created")) + case Left(failure) => + fail(failure.failure) + } + } + + +} diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index eb4450b6..55e62808 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -7,6 +7,7 @@ import za.co.absa.fadb.DBFunction._ import za.co.absa.fadb.DBSchema import za.co.absa.fadb.doobie.Results.{FailedResult, ResultWithStatus, SuccessfulResult} import za.co.absa.fadb.status.FunctionStatus +import za.co.absa.fadb.status.handling.StatusHandling import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling import scala.util.{Failure, Success} @@ -47,7 +48,7 @@ private [doobie] trait DoobieFunction[I, R] { * @tparam I the input type of the function * @tparam R the result type of the function */ -private [doobie] trait DoobieFunctionWithStatusSupport[I, R] extends StandardStatusHandling { +private [doobie] trait DoobieFunctionWithStatusSupport[I, R] extends StatusHandling { /** * The `Read[R]` instance used to read the query result into `R`. @@ -163,61 +164,7 @@ object DoobieFunction { } } } - // ==Example== - // {{{ - // CREATE OR REPLACE FUNCTION runs.create_actor( - // IN i_first_name TEXT, - // IN i_last_name TEXT, - // OUT status INTEGER, - // OUT status_text TEXT, - // OUT o_actor_id INTEGER - // ) RETURNS record AS - // $$ - // BEGIN - // INSERT INTO runs.actors(first_name, last_name) - // VALUES (i_first_name, i_last_name) - // RETURNING actor_id INTO o_actor_id; - // - // status := 11; - // status_text := 'Actor created'; - // - // RETURN; - // END; - // $$ - // LANGUAGE plpgsql; - // - // case class CreateActorRequestBody(firstName: String, lastName: String) - // - // class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine) - // extends DoobieFunction.DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int] { - // - // override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): fragment.Fragment = - // sql"SELECT * FROM create_actor(${values.firstName}, ${values.lastName})" - // } - // - // class DoobieTest extends AnyFunSuite { - // import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ - // object Runs extends DBSchema - // - // private val transactor = Transactor.fromDriverManager[IO]( - // "org.postgresql.ds.PGSimpleDataSource", - // "jdbc:postgresql://localhost:5432/movies", - // "postgres", - // "postgres" - // ) - // - // private val createActor = new CreateActor()(Runs, new DoobiePgEngine(transactor)) - // - // test("DoobieTest with status handling") { - // val requestBody = CreateActorRequestBody("Pavel", "Marek") - // createActor.applyWithStatus(requestBody).unsafeRunSync() match { - // case Right(success) => - // assert(success.functionStatus == status.FunctionStatus(11, "Actor created")) - // case Left(failure) => - // fail(failure.failure) - // } - // } - // } - // }}} + + // for an example see DoobieFunctionWithStatusSupportTest } diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala b/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala index 00575ef0..637ba2af 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala @@ -26,6 +26,7 @@ import org.scalatest.flatspec.AsyncFlatSpec import java.time.{Duration, LocalDate, LocalDateTime, LocalTime, OffsetDateTime, ZonedDateTime} import java.util.UUID +import scala.concurrent.Future class FaDbPostgresProfileSuite extends AsyncFlatSpec { @@ -53,7 +54,7 @@ class FaDbPostgresProfileSuite extends AsyncFlatSpec { ) class TestFunction(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) - extends DBSingleResultFunction[InputOutput, InputOutput, SlickPgEngine] + extends DBSingleResultFunction[InputOutput, InputOutput, SlickPgEngine, Future] with SlickFunction[InputOutput, InputOutput] { override protected def sql(values: InputOutput): SQLActionBuilder = { @@ -136,7 +137,7 @@ class FaDbPostgresProfileSuite extends AsyncFlatSpec { ) class TestFunction(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) - extends DBSingleResultFunction[InputOutput, InputOutput, SlickPgEngine] + extends DBSingleResultFunction[InputOutput, InputOutput, SlickPgEngine, Future] with SlickFunction[InputOutput, InputOutput] { override protected def sql(values: InputOutput): SQLActionBuilder = { From d12d96a2e22a9d92aa8f464a438d0b7d5af37839 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Mon, 4 Dec 2023 13:47:01 +0100 Subject: [PATCH 07/90] integration tests --- doobie/src/it/database/actors.ddl | 5 +++ doobie/src/it/database/create_actor.sql | 37 +++++++++++++++++++ doobie/src/it/database/get_actor_by_id.sql | 35 ++++++++++++++++++ doobie/src/it/database/get_actors.sql | 20 ++++++++++ .../DoobieMultipleResultFunctionTest.scala | 31 ++++++++++++++++ .../DoobieOptionalResultFunctionTest.scala | 30 +++++++++++++++ .../DoobieSingleResultFunctionTest.scala | 28 ++++++++++++++ ...ResultFunctionWithStatusSupportTest.scala} | 28 ++------------ .../za/co/absa/fadb/doobie/DoobieTest.scala | 27 ++++++++++++++ .../co/absa/fadb/doobie/DoobieFunction.scala | 1 - 10 files changed, 216 insertions(+), 26 deletions(-) create mode 100644 doobie/src/it/database/actors.ddl create mode 100644 doobie/src/it/database/create_actor.sql create mode 100644 doobie/src/it/database/get_actor_by_id.sql create mode 100644 doobie/src/it/database/get_actors.sql create mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala create mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala create mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala rename doobie/src/it/scala/za/co/absa/fadb/doobie/{DoobieFunctionWithStatusSupportTest.scala => DoobieSingleResultFunctionWithStatusSupportTest.scala} (61%) create mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala diff --git a/doobie/src/it/database/actors.ddl b/doobie/src/it/database/actors.ddl new file mode 100644 index 00000000..5885bfb7 --- /dev/null +++ b/doobie/src/it/database/actors.ddl @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS runs.actors ( + actor_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + first_name VARCHAR(150) NOT NULL, + last_name VARCHAR(150) NOT NULL +); \ No newline at end of file diff --git a/doobie/src/it/database/create_actor.sql b/doobie/src/it/database/create_actor.sql new file mode 100644 index 00000000..77a3d1be --- /dev/null +++ b/doobie/src/it/database/create_actor.sql @@ -0,0 +1,37 @@ +-- Function: runs.create_actor(TEXT, TEXT) +-- This function creates a new actor in the 'runs.actors' table. + +-- Parameters: +-- i_first_name (IN): The first name of the actor. +-- i_last_name (IN): The last name of the actor. + +-- Output: +-- status (OUT): The status of the operation. Returns 11 if the actor is successfully created. +-- status_text (OUT): The status text of the operation. Returns 'Actor created' if the actor is successfully created. + +-- Returns: +-- A record containing the status and status text of the operation. + +-- Example: +-- SELECT * FROM runs.create_actor('John', 'Doe'); + +CREATE OR REPLACE FUNCTION runs.create_actor( + IN i_first_name TEXT, + IN i_last_name TEXT, + OUT status INTEGER, + OUT status_text TEXT, + OUT o_actor_id INTEGER +) RETURNS record AS +$$ +BEGIN + INSERT INTO runs.actors(first_name, last_name) + VALUES (i_first_name, i_last_name) + RETURNING actor_id INTO o_actor_id; + + status := 11; + status_text := 'Actor created'; + + RETURN; +END; +$$ +LANGUAGE plpgsql; \ No newline at end of file diff --git a/doobie/src/it/database/get_actor_by_id.sql b/doobie/src/it/database/get_actor_by_id.sql new file mode 100644 index 00000000..96f70006 --- /dev/null +++ b/doobie/src/it/database/get_actor_by_id.sql @@ -0,0 +1,35 @@ +/* + * Function: runs.get_actor_by_id + * + * Description: + * This function retrieves an actor from the 'runs.actors' table by their ID. + * + * Parameters: + * i_actor_id - The ID of the actor to retrieve. + * + * Returns: + * A table with the following columns: + * actor_id - The ID of the actor. + * first_name - The first name of the actor. + * last_name - The last name of the actor. + * + * Example: + * SELECT * FROM runs.get_actor_by_id(1); + * + * This will return the actor with ID 1, if he/she exists. + */ +CREATE OR REPLACE FUNCTION runs.get_actor_by_id( + i_actor_id INTEGER +) RETURNS TABLE ( + actor_id INTEGER, + first_name VARCHAR(150), + last_name VARCHAR(150) +) AS +$$ +BEGIN + RETURN QUERY SELECT A.actor_id, A.first_name, A.last_name + FROM runs.actors A + WHERE A.actor_id = i_actor_id; +END; +$$ +LANGUAGE plpgsql; \ No newline at end of file diff --git a/doobie/src/it/database/get_actors.sql b/doobie/src/it/database/get_actors.sql new file mode 100644 index 00000000..f777464b --- /dev/null +++ b/doobie/src/it/database/get_actors.sql @@ -0,0 +1,20 @@ +CREATE OR REPLACE FUNCTION runs.get_actors( + i_first_name TEXT, + i_last_name TEXT +) RETURNS TABLE ( + actor_id INTEGER, + first_name VARCHAR(150), + last_name VARCHAR(150) + ) AS +$$ +BEGIN + RETURN QUERY SELECT A.actor_id, A.first_name, A.last_name + FROM runs.actors A + WHERE + (i_first_name IS NULL OR A.first_name = i_first_name) + AND + (i_last_name IS NULL OR A.last_name = i_last_name) + ORDER BY A.actor_id ASC; +END; +$$ +LANGUAGE plpgsql; \ No newline at end of file diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala new file mode 100644 index 00000000..5e263baa --- /dev/null +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala @@ -0,0 +1,31 @@ +package za.co.absa.fadb.doobie + +import cats.effect.unsafe.implicits.global +import doobie.implicits.toSqlInterpolator +import doobie.util.Read +import doobie.util.fragment.Fragment +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.doobie.DoobieFunction.DoobieMultipleResultFunction + +class DoobieMultipleResultFunctionTest extends AnyFunSuite with DoobieTest { + + case class Actor(actorId: Int, firstName: String, lastName: String) + case class GetActorsQueryParameters(firstName: Option[String], lastName: Option[String]) + + class GetActors(implicit schema: DBSchema, dbEngine: DoobiePgEngine) + extends DoobieMultipleResultFunction[GetActorsQueryParameters, Actor] { + + override def sql(values: GetActorsQueryParameters)(implicit read: Read[Actor]): Fragment = + sql"SELECT actor_id, first_name, last_name FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" + } + + private val getActors = new GetActors()(Runs, new DoobiePgEngine(transactor)) + + test("DoobieTest") { + val expectedResultElem = Actor(49, "Pavel", "Marek") + val results = getActors.apply(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))).unsafeRunSync() + assert(results.contains(expectedResultElem)) + } + +} diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala new file mode 100644 index 00000000..20a60e52 --- /dev/null +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala @@ -0,0 +1,30 @@ +package za.co.absa.fadb.doobie + +import cats.effect.unsafe.implicits.global +import doobie.implicits.toSqlInterpolator +import doobie.util.Read +import doobie.util.fragment.Fragment +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.doobie.DoobieFunction.DoobieOptionalResultFunction + +class DoobieOptionalResultFunctionTest extends AnyFunSuite with DoobieTest { + + case class Actor(actorId: Int, firstName: String, lastName: String) + + class GetActorById(implicit schema: DBSchema, dbEngine: DoobiePgEngine) + extends DoobieOptionalResultFunction[Int, Actor] { + + override def sql(values: Int)(implicit read: Read[Actor]): Fragment = + sql"SELECT actor_id, first_name, last_name FROM ${Fragment.const(functionName)}($values)" + } + + private val createActor = new GetActorById()(Runs, new DoobiePgEngine(transactor)) + + test("DoobieTest") { + val expectedResult = Some(Actor(49, "Pavel", "Marek")) + val result = createActor.apply(49).unsafeRunSync() + assert(expectedResult == result) + } + +} diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala new file mode 100644 index 00000000..0ba58587 --- /dev/null +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala @@ -0,0 +1,28 @@ +package za.co.absa.fadb.doobie + +import cats.effect.unsafe.implicits.global +import doobie.implicits.toSqlInterpolator +import doobie.util.Read +import doobie.util.fragment.Fragment +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunction + +class DoobieSingleResultFunctionTest extends AnyFunSuite with DoobieTest { + + case class CreateActorRequestBody(firstName: String, lastName: String) + + class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine) + extends DoobieSingleResultFunction[CreateActorRequestBody, Int] { + + override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = + sql"SELECT o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" + } + + private val createActor = new CreateActor()(Runs, new DoobiePgEngine(transactor)) + + test("DoobieTest") { + assert(createActor.apply(CreateActorRequestBody("Pavel", "Marek")).unsafeRunSync().isInstanceOf[Int]) + } + +} diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusSupportTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala similarity index 61% rename from doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusSupportTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala index cdb3eb64..b1aba0e7 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusSupportTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala @@ -1,18 +1,16 @@ package za.co.absa.fadb.doobie -import cats.effect.IO import cats.effect.unsafe.implicits.global +import doobie.Fragment import doobie.implicits.toSqlInterpolator import doobie.util.Read -import doobie.util.log.{LogEvent, LogHandler} -import doobie.{Fragment, Transactor} import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStatusSupport import za.co.absa.fadb.status.FunctionStatus import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling -class DoobieFunctionWithStatusSupportTest extends AnyFunSuite { +class DoobieSingleResultFunctionWithStatusSupportTest extends AnyFunSuite with DoobieTest { case class CreateActorRequestBody(firstName: String, lastName: String) @@ -21,27 +19,9 @@ class DoobieFunctionWithStatusSupportTest extends AnyFunSuite { with StandardStatusHandling { override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = - sql"SELECT * FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" + sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" } - import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ - object Runs extends DBSchema - - val printSqlLogHandler: LogHandler[IO] = new LogHandler[IO] { - def run(logEvent: LogEvent): IO[Unit] = - IO { - println(logEvent.sql) - } - } - - private val transactor = Transactor.fromDriverManager[IO]( - "org.postgresql.ds.PGSimpleDataSource", - "jdbc:postgresql://localhost:5432/movies", - "postgres", - "postgres", - Some(printSqlLogHandler) - ) - private val createActor = new CreateActor()(Runs, new DoobiePgEngine(transactor)) test("DoobieTest with status handling") { @@ -53,6 +33,4 @@ class DoobieFunctionWithStatusSupportTest extends AnyFunSuite { fail(failure.failure) } } - - } diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala new file mode 100644 index 00000000..296eb6e1 --- /dev/null +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala @@ -0,0 +1,27 @@ +package za.co.absa.fadb.doobie + +import cats.effect.IO +import doobie.util.log.{LogEvent, LogHandler} +import doobie.util.transactor.Transactor +import za.co.absa.fadb.DBSchema + +trait DoobieTest { + import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ + + object Runs extends DBSchema + + val printSqlLogHandler: LogHandler[IO] = new LogHandler[IO] { + def run(logEvent: LogEvent): IO[Unit] = + IO { + println(logEvent.sql) + } + } + + protected val transactor = Transactor.fromDriverManager[IO]( + "org.postgresql.ds.PGSimpleDataSource", + "jdbc:postgresql://localhost:5432/movies", + "postgres", + "postgres", + Some(printSqlLogHandler) + ) +} diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index 55e62808..ed7b3130 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -8,7 +8,6 @@ import za.co.absa.fadb.DBSchema import za.co.absa.fadb.doobie.Results.{FailedResult, ResultWithStatus, SuccessfulResult} import za.co.absa.fadb.status.FunctionStatus import za.co.absa.fadb.status.handling.StatusHandling -import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling import scala.util.{Failure, Success} From 804035d131453cc0dfccc3537415ea7fc6f27714 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Mon, 4 Dec 2023 14:12:24 +0100 Subject: [PATCH 08/90] integration tests --- .../DoobieMultipleResultFunctionTest.scala | 4 --- .../DoobieOptionalResultFunctionTest.scala | 2 -- .../DoobieSingleResultFunctionTest.scala | 3 -- ...eResultFunctionWithStatusSupportTest.scala | 2 -- .../za/co/absa/fadb/doobie/DoobieTest.scala | 5 ++- slick/src/it/resources/application.conf | 4 +-- .../scala/za/co/absa/fadb/slick/Actor.scala | 3 ++ .../absa/fadb/slick/ActorSlickConverter.scala | 17 +++++++++ .../SlickMultipleResultFunctionTest.scala | 35 ++++++++++++++++++ .../SlickOptionalResultFunctionTest.scala | 36 +++++++++++++++++++ .../za/co/absa/fadb/slick/SlickTest.scala | 13 +++++++ 11 files changed, 110 insertions(+), 14 deletions(-) create mode 100644 slick/src/it/scala/za/co/absa/fadb/slick/Actor.scala create mode 100644 slick/src/it/scala/za/co/absa/fadb/slick/ActorSlickConverter.scala create mode 100644 slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala create mode 100644 slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala create mode 100644 slick/src/it/scala/za/co/absa/fadb/slick/SlickTest.scala diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala index 5e263baa..04bd5322 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala @@ -10,9 +10,6 @@ import za.co.absa.fadb.doobie.DoobieFunction.DoobieMultipleResultFunction class DoobieMultipleResultFunctionTest extends AnyFunSuite with DoobieTest { - case class Actor(actorId: Int, firstName: String, lastName: String) - case class GetActorsQueryParameters(firstName: Option[String], lastName: Option[String]) - class GetActors(implicit schema: DBSchema, dbEngine: DoobiePgEngine) extends DoobieMultipleResultFunction[GetActorsQueryParameters, Actor] { @@ -27,5 +24,4 @@ class DoobieMultipleResultFunctionTest extends AnyFunSuite with DoobieTest { val results = getActors.apply(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))).unsafeRunSync() assert(results.contains(expectedResultElem)) } - } diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala index 20a60e52..f4a3bff9 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala @@ -10,8 +10,6 @@ import za.co.absa.fadb.doobie.DoobieFunction.DoobieOptionalResultFunction class DoobieOptionalResultFunctionTest extends AnyFunSuite with DoobieTest { - case class Actor(actorId: Int, firstName: String, lastName: String) - class GetActorById(implicit schema: DBSchema, dbEngine: DoobiePgEngine) extends DoobieOptionalResultFunction[Int, Actor] { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala index 0ba58587..1f953e4b 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala @@ -10,8 +10,6 @@ import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunction class DoobieSingleResultFunctionTest extends AnyFunSuite with DoobieTest { - case class CreateActorRequestBody(firstName: String, lastName: String) - class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine) extends DoobieSingleResultFunction[CreateActorRequestBody, Int] { @@ -24,5 +22,4 @@ class DoobieSingleResultFunctionTest extends AnyFunSuite with DoobieTest { test("DoobieTest") { assert(createActor.apply(CreateActorRequestBody("Pavel", "Marek")).unsafeRunSync().isInstanceOf[Int]) } - } diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala index b1aba0e7..0f047f84 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala @@ -12,8 +12,6 @@ import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling class DoobieSingleResultFunctionWithStatusSupportTest extends AnyFunSuite with DoobieTest { - case class CreateActorRequestBody(firstName: String, lastName: String) - class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine) extends DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int] with StandardStatusHandling { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala index 296eb6e1..043001c8 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala @@ -6,8 +6,11 @@ import doobie.util.transactor.Transactor import za.co.absa.fadb.DBSchema trait DoobieTest { - import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ + case class Actor(actorId: Int, firstName: String, lastName: String) + case class GetActorsQueryParameters(firstName: Option[String], lastName: Option[String]) + case class CreateActorRequestBody(firstName: String, lastName: String) + import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ object Runs extends DBSchema val printSqlLogHandler: LogHandler[IO] = new LogHandler[IO] { diff --git a/slick/src/it/resources/application.conf b/slick/src/it/resources/application.conf index 14ffec23..1dea17b8 100644 --- a/slick/src/it/resources/application.conf +++ b/slick/src/it/resources/application.conf @@ -4,9 +4,9 @@ postgrestestdb = { properties = { serverName = "localhost" portNumber = "5432" - databaseName = "postgres" + databaseName = "movies"//"postgres" user = "postgres" - password = "changeme" + password = "postgres" } numThreads = 10 } diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/Actor.scala b/slick/src/it/scala/za/co/absa/fadb/slick/Actor.scala new file mode 100644 index 00000000..54381be4 --- /dev/null +++ b/slick/src/it/scala/za/co/absa/fadb/slick/Actor.scala @@ -0,0 +1,3 @@ +package za.co.absa.fadb.slick + +case class Actor(actorId: Int, firstName: String, lastName: String) diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/ActorSlickConverter.scala b/slick/src/it/scala/za/co/absa/fadb/slick/ActorSlickConverter.scala new file mode 100644 index 00000000..863cb189 --- /dev/null +++ b/slick/src/it/scala/za/co/absa/fadb/slick/ActorSlickConverter.scala @@ -0,0 +1,17 @@ +package za.co.absa.fadb.slick + +import slick.jdbc.{GetResult, PositionedResult} + +/** + * A trait representing a converter from a Slick PositionedResult to an Actor. + * The trait is to be mixed into a SlickFunction returning an Actor. + */ +trait ActorSlickConverter { + + protected def slickConverter: GetResult[Actor] = { + def converter(r: PositionedResult): Actor = { + Actor(r.<<, r.<<, r.<<) + } + GetResult(converter) + } +} diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala new file mode 100644 index 00000000..fdb465e3 --- /dev/null +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala @@ -0,0 +1,35 @@ +package za.co.absa.fadb.slick + +import cats.implicits._ +import org.scalatest.funsuite.AnyFunSuite +import slick.jdbc.SQLActionBuilder +import za.co.absa.fadb.DBFunction.DBMultipleResultFunction +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.slick.FaDbPostgresProfile.api._ + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration.DurationInt +import scala.concurrent.{Await, Future} + +class SlickMultipleResultFunctionTest extends AnyFunSuite with SlickTest { + + class GetActors(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) + extends DBMultipleResultFunction[GetActorsQueryParameters, Actor, SlickPgEngine, Future] + with SlickFunction[GetActorsQueryParameters, Actor] + with ActorSlickConverter { + + override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("actor_id", "first_name", "last_name") + + override protected def sql(values: GetActorsQueryParameters): SQLActionBuilder = { + sql"""SELECT #$selectEntry FROM #$functionName(${values.firstName},${values.lastName}) #$alias;""" + } + } + + private val getActors = new GetActors()(Runs, new SlickPgEngine(db)) + + test("SlickTest") { + val expectedResultElem = Actor(49, "Pavel", "Marek") + val results = getActors.apply(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))) + assert(Await.result(results, 5.seconds).contains(expectedResultElem)) + } +} diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala new file mode 100644 index 00000000..056df720 --- /dev/null +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala @@ -0,0 +1,36 @@ +package za.co.absa.fadb.slick + +import cats.implicits._ + +import scala.concurrent.ExecutionContext.Implicits.global +import org.scalatest.funsuite.AnyFunSuite +import slick.jdbc.SQLActionBuilder +import za.co.absa.fadb.DBFunction.DBOptionalResultFunction +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.slick.FaDbPostgresProfile.api._ + +import scala.concurrent.{Await, Future} +import scala.concurrent.duration.DurationInt + +class SlickOptionalResultFunctionTest extends AnyFunSuite with SlickTest { + + class GetActorById(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) + extends DBOptionalResultFunction[Int, Actor, SlickPgEngine, Future] + with SlickFunction[Int, Actor] + with ActorSlickConverter { + + override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("actor_id", "first_name", "last_name") + + override protected def sql(values: Int): SQLActionBuilder = { + sql"""SELECT #$selectEntry FROM #$functionName($values) #$alias;""" + } + } + + private val getActorById = new GetActorById()(Runs, new SlickPgEngine(db)) + + test("SlickTest") { + val expectedResultElem = Some(Actor(49, "Pavel", "Marek")) + val results = getActorById.apply(49) + assert(Await.result(results, 5.seconds) == expectedResultElem) + } +} diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickTest.scala new file mode 100644 index 00000000..67e487e7 --- /dev/null +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickTest.scala @@ -0,0 +1,13 @@ +package za.co.absa.fadb.slick + +import slick.jdbc.JdbcBackend.Database +import za.co.absa.fadb.DBSchema + +trait SlickTest { + case class GetActorsQueryParameters(firstName: Option[String], lastName: Option[String]) + + import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ + object Runs extends DBSchema + + val db = Database.forConfig("postgrestestdb") +} From d83b2744b2086b911a92d7cab1a4f228ac59edd0 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 5 Dec 2023 09:00:12 +0100 Subject: [PATCH 09/90] minors --- .../co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala | 2 +- .../co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala | 2 +- .../za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala | 2 +- .../za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala | 2 +- .../za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala index 04bd5322..de6578d0 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala @@ -21,7 +21,7 @@ class DoobieMultipleResultFunctionTest extends AnyFunSuite with DoobieTest { test("DoobieTest") { val expectedResultElem = Actor(49, "Pavel", "Marek") - val results = getActors.apply(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))).unsafeRunSync() + val results = getActors(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))).unsafeRunSync() assert(results.contains(expectedResultElem)) } } diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala index f4a3bff9..7a20205c 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala @@ -21,7 +21,7 @@ class DoobieOptionalResultFunctionTest extends AnyFunSuite with DoobieTest { test("DoobieTest") { val expectedResult = Some(Actor(49, "Pavel", "Marek")) - val result = createActor.apply(49).unsafeRunSync() + val result = createActor(49).unsafeRunSync() assert(expectedResult == result) } diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala index 1f953e4b..6ba9c4eb 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala @@ -20,6 +20,6 @@ class DoobieSingleResultFunctionTest extends AnyFunSuite with DoobieTest { private val createActor = new CreateActor()(Runs, new DoobiePgEngine(transactor)) test("DoobieTest") { - assert(createActor.apply(CreateActorRequestBody("Pavel", "Marek")).unsafeRunSync().isInstanceOf[Int]) + assert(createActor(CreateActorRequestBody("Pavel", "Marek")).unsafeRunSync().isInstanceOf[Int]) } } diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala index fdb465e3..68429128 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala @@ -29,7 +29,7 @@ class SlickMultipleResultFunctionTest extends AnyFunSuite with SlickTest { test("SlickTest") { val expectedResultElem = Actor(49, "Pavel", "Marek") - val results = getActors.apply(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))) + val results = getActors(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))) assert(Await.result(results, 5.seconds).contains(expectedResultElem)) } } diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala index 056df720..1fc95d09 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala @@ -30,7 +30,7 @@ class SlickOptionalResultFunctionTest extends AnyFunSuite with SlickTest { test("SlickTest") { val expectedResultElem = Some(Actor(49, "Pavel", "Marek")) - val results = getActorById.apply(49) + val results = getActorById(49) assert(Await.result(results, 5.seconds) == expectedResultElem) } } From 491e23587dde8a09066ad5c388d6ddb5f7a2e446 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 5 Dec 2023 09:03:59 +0100 Subject: [PATCH 10/90] enceladus example fixed --- .../examples/enceladus/DatasetSchema.scala | 247 +++++++++--------- 1 file changed, 125 insertions(+), 122 deletions(-) diff --git a/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala b/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala index 0ae6fc0e..42eac7d4 100644 --- a/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala +++ b/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala @@ -1,122 +1,125 @@ -///* -// * Copyright 2022 ABSA Group Limited -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -// -//package za.co.absa.fadb.examples.enceladus -// -//import za.co.absa.fadb.DBSchema -//import za.co.absa.fadb.slick.{SlickPgEngine, SlickFunction, SlickFunctionWithStatusSupport} -//import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention -//import slick.jdbc.{GetResult, SQLActionBuilder} -//import slick.jdbc.PostgresProfile.api._ -// -//import java.sql.Timestamp -//import scala.concurrent.Future -//import DatasetSchema._ -//import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBSingleResultFunction} -//import za.co.absa.fadb.status.handling.implementations.UserDefinedStatusHandling -// -///* The Schema doesn't need the dBEngine directly, but it seems cleaner this way to hand it over to schema's functions */ -//class DatasetSchema(implicit engine: SlickPgEngine) extends DBSchema { -// -// val addSchema = new AddSchema -// val getSchema = new GetSchema -// val list = new List -//} -// -// -//object DatasetSchema { -// -// case class SchemaInput(schemaName: String, -// schemaVersion: Int, -// schemaDescription: Option[String], -// fields: Option[String], -// userName: String) -// -// case class Schema(idSchemaVersion: Long, -// schemaName: String, -// schemaVersion: Int, -// schemaDescription: Option[String], -// fields: Option[String], -// createdBy: String, -// createdWhen: Timestamp, -// updatedBy: String, -// updatedWhen: Timestamp, -// lockedBy: Option[String], -// lockedWhen: Option[Timestamp], -// deletedBy: Option[String], -// deletedWhen: Option[Timestamp]) -// -// case class SchemaHeader(entityName: String, entityLatestVersion: Int) -// -// final class AddSchema(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) -// extends DBSingleResultFunction[SchemaInput, Long, SlickPgEngine] -// with SlickFunctionWithStatusSupport[SchemaInput, Long] -// with UserDefinedStatusHandling { -// -// override protected def sql(values: SchemaInput): SQLActionBuilder = { -// sql"""SELECT A.status, A.status_text, A.id_schema_version -// FROM #$functionName(${values.schemaName}, ${values.schemaVersion}, ${values.schemaDescription}, -// ${values.fields}::JSONB, ${values.userName} -// ) A;""" -// } -// -// override protected def slickConverter: GetResult[Long] = GetResult.GetLong -// -// override def OKStatuses: Set[Integer] = Set(201) -// -// } -// -// final class GetSchema(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) -// extends DBSingleResultFunction[(String, Option[Int]), Schema, SlickPgEngine] -// with SlickFunctionWithStatusSupport[(String, Option[Int]), Schema] -// with UserDefinedStatusHandling { -// -// /* This is an example of how to deal with overloaded DB functions - see different input type: Long vs what's in the class type: (String, Option[Int]) */ -// def apply(id: Long): Future[Schema] = { -// val sql = -// sql"""SELECT A.* -// FROM #$functionName($id) A;""" -// -// val slickQuery = new dBEngine.QueryType(sql, slickConverter) -// dBEngine.fetchHead[Schema](slickQuery) -// } -// -// override protected def sql(values: (String, Option[Int])): SQLActionBuilder = { -// sql"""SELECT A.* -// FROM #$functionName(${values._1}, ${values._2}) A;""" -// } -// -// override protected val slickConverter: GetResult[Schema] = GetResult{r => -// Schema(r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<) -// } -// -// override val OKStatuses: Set[Integer] = Set(200) -// } -// -// final class List(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) -// extends DBMultipleResultFunction[Boolean, SchemaHeader, SlickPgEngine]() -// with SlickFunction[Boolean, SchemaHeader] { -// -// override def apply(values: Boolean = false): Future[Seq[SchemaHeader]] = super.apply(values) -// -// override protected def sql(values: Boolean): SQLActionBuilder = { -// sql"""SELECT A.entity_name, A.entity_latest_version -// FROM #$functionName($values) as A;""" -// } -// -// override protected val slickConverter: GetResult[SchemaHeader] = GetResult(r => {SchemaHeader(r.<<, r.<<)}) -// } -//} +/* +* Copyright 2022 ABSA Group Limited +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package za.co.absa.fadb.examples.enceladus + +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.slick.{SlickFunction, SlickFunctionWithStatusSupport, SlickPgEngine} +import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention +import slick.jdbc.{GetResult, SQLActionBuilder} +import slick.jdbc.PostgresProfile.api._ + +import java.sql.Timestamp +import scala.concurrent.Future +import DatasetSchema._ +import cats.implicits._ +import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBSingleResultFunction} +import za.co.absa.fadb.status.handling.implementations.UserDefinedStatusHandling + +import scala.concurrent.ExecutionContext.Implicits.global + +/* The Schema doesn't need the dBEngine directly, but it seems cleaner this way to hand it over to schema's functions */ +class DatasetSchema(implicit engine: SlickPgEngine) extends DBSchema { + + val addSchema = new AddSchema + val getSchema = new GetSchema + val list = new List +} + + +object DatasetSchema { + + case class SchemaInput(schemaName: String, + schemaVersion: Int, + schemaDescription: Option[String], + fields: Option[String], + userName: String) + + case class Schema(idSchemaVersion: Long, + schemaName: String, + schemaVersion: Int, + schemaDescription: Option[String], + fields: Option[String], + createdBy: String, + createdWhen: Timestamp, + updatedBy: String, + updatedWhen: Timestamp, + lockedBy: Option[String], + lockedWhen: Option[Timestamp], + deletedBy: Option[String], + deletedWhen: Option[Timestamp]) + + case class SchemaHeader(entityName: String, entityLatestVersion: Int) + + final class AddSchema(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) + extends DBSingleResultFunction[SchemaInput, Long, SlickPgEngine, Future] + with SlickFunctionWithStatusSupport[SchemaInput, Long] + with UserDefinedStatusHandling { + + override protected def sql(values: SchemaInput): SQLActionBuilder = { + sql"""SELECT A.status, A.status_text, A.id_schema_version + FROM #$functionName(${values.schemaName}, ${values.schemaVersion}, ${values.schemaDescription}, + ${values.fields}::JSONB, ${values.userName} + ) A;""" + } + + override protected def slickConverter: GetResult[Long] = GetResult.GetLong + + override def OKStatuses: Set[Integer] = Set(201) + + } + + final class GetSchema(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) + extends DBSingleResultFunction[(String, Option[Int]), Schema, SlickPgEngine, Future] + with SlickFunctionWithStatusSupport[(String, Option[Int]), Schema] + with UserDefinedStatusHandling { + + /* This is an example of how to deal with overloaded DB functions - see different input type: Long vs what's in the class type: (String, Option[Int]) */ + def apply(id: Long): Future[Schema] = { + val sql = + sql"""SELECT A.* + FROM #$functionName($id) A;""" + + val slickQuery = new dBEngine.QueryType(sql, slickConverter) + dBEngine.fetchHead[Schema](slickQuery) + } + + override protected def sql(values: (String, Option[Int])): SQLActionBuilder = { + sql"""SELECT A.* + FROM #$functionName(${values._1}, ${values._2}) A;""" + } + + override protected val slickConverter: GetResult[Schema] = GetResult{r => + Schema(r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<) + } + + override val OKStatuses: Set[Integer] = Set(200) + } + + final class List(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) + extends DBMultipleResultFunction[Boolean, SchemaHeader, SlickPgEngine, Future]() + with SlickFunction[Boolean, SchemaHeader] { + + override def apply(values: Boolean = false): Future[Seq[SchemaHeader]] = super.apply(values) + + override protected def sql(values: Boolean): SQLActionBuilder = { + sql"""SELECT A.entity_name, A.entity_latest_version + FROM #$functionName($values) as A;""" + } + + override protected val slickConverter: GetResult[SchemaHeader] = GetResult(r => {SchemaHeader(r.<<, r.<<)}) + } +} From 088065f1cb6a88b3136e939fc89609a4b0d85bc4 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 5 Dec 2023 10:31:20 +0100 Subject: [PATCH 11/90] build related --- build.sbt | 3 +++ project/Dependencies.scala | 2 +- slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index d69203c7..40f03f70 100644 --- a/build.sbt +++ b/build.sbt @@ -97,6 +97,9 @@ lazy val faDBDoobie = (project in file("doobie")) scalacOptions ++= commonScalacOptions, (Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, // printScalaVersion is run with compile Defaults.itSettings, + compile / skip := !scalaVersion.value.startsWith("2.12"), // skip if not Scala 2.12 (does not work, but publishM2 does) + crossScalaVersions := Seq(scala212), + publishM2 := (if (scalaVersion.value.startsWith("2.12")) publishM2.value else ()) // publish only for Scala 2.12 ).dependsOn(faDbCore) .settings( jacocoReportSettings := commonJacocoReportSettings.withTitle(s"doobie:slick Jacoco Report - scala:${scalaVersion.value}"), diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 76b53f07..7d10b97f 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -19,7 +19,7 @@ import sbt._ object Dependencies { private def commonDependencies(scalaVersion: String): Seq[ModuleID] = Seq( - "org.typelevel" %% "cats-core" % "2.9.0", + "org.typelevel" %% "cats-core" % "2.0.0", // 2.0.0 latest for Scala 2.11, 2.9.0 used by Doobie "org.scalatest" %% "scalatest" % "3.1.0" % "test,it", "org.scalatest" %% "scalatest-flatspec" % "3.2.0" % "test,it", "org.scalatestplus" %% "mockito-1-10" % "3.1.0.0" % "test,it" diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala index 4d7f8e0d..fce7a6d4 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala @@ -22,6 +22,8 @@ import za.co.absa.fadb.DBEngine import scala.concurrent.{ExecutionContext, Future} import slick.jdbc.PostgresProfile.api._ +import cats.implicits._ + import scala.language.higherKinds /** From d766c2501bf5fb07c1bf55e2710e5718c5b06ae2 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 5 Dec 2023 10:48:08 +0100 Subject: [PATCH 12/90] alternative constructors --- .../scala/za/co/absa/fadb/DBFunction.scala | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index b0d6399a..fbc71a06 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -35,6 +35,12 @@ import scala.language.higherKinds abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) (implicit val schema: DBSchema, val dBEngine: E) extends DBFunctionFabric { + // A constructor that takes only the mandatory parameters and uses default values for the optional ones + def this()(implicit schema: DBSchema, dBEngine: E) = this(None) + + // A constructor that allows specifying the function name as a string, but not as an option + def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) + /** * Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the * DBEngine specific mix-in. @@ -86,6 +92,12 @@ object DBFunction { (implicit schema: DBSchema, dBEngine: E) extends DBFunction[I, R, E, F](functionNameOverride) { + // A constructor that takes only the mandatory parameters and uses default values for the optional ones + def this()(implicit schema: DBSchema, dBEngine: E) = this(None) + + // A constructor that allows specifying the function name as a string, but not as an option + def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) + /** * For easy and convenient execution of the DB function call * @param values - the values to pass over to the database function @@ -110,6 +122,12 @@ object DBFunction { (implicit schema: DBSchema, dBEngine: E) extends DBFunction[I, R, E, F](functionNameOverride) { + // A constructor that takes only the mandatory parameters and uses default values for the optional ones + def this()(implicit schema: DBSchema, dBEngine: E) = this(None) + + // A constructor that allows specifying the function name as a string, but not as an option + def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) + /** * For easy and convenient execution of the DB function call * @param values - the values to pass over to the database function @@ -133,6 +151,12 @@ object DBFunction { (implicit schema: DBSchema, dBEngine: E) extends DBFunction[I, R, E, F](functionNameOverride) { + // A constructor that takes only the mandatory parameters and uses default values for the optional ones + def this()(implicit schema: DBSchema, dBEngine: E) = this(None) + + // A constructor that allows specifying the function name as a string, but not as an option + def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) + /** * For easy and convenient execution of the DB function call * @param values - the values to pass over to the database function From 6f0845ebbb1365b4b727c066e7ebf95063c9eb86 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 5 Dec 2023 11:03:03 +0100 Subject: [PATCH 13/90] licences added --- .../DoobieMultipleResultFunctionTest.scala | 16 ++++++++++++++++ .../DoobieOptionalResultFunctionTest.scala | 16 ++++++++++++++++ .../doobie/DoobieSingleResultFunctionTest.scala | 16 ++++++++++++++++ ...ngleResultFunctionWithStatusSupportTest.scala | 16 ++++++++++++++++ .../za/co/absa/fadb/doobie/DoobieTest.scala | 16 ++++++++++++++++ .../za/co/absa/fadb/doobie/DoobieFunction.scala | 16 ++++++++++++++++ .../za/co/absa/fadb/doobie/DoobiePgEngine.scala | 16 ++++++++++++++++ .../za/co/absa/fadb/doobie/DoobieQuery.scala | 16 ++++++++++++++++ .../scala/za/co/absa/fadb/doobie/Results.scala | 16 ++++++++++++++++ .../it/scala/za/co/absa/fadb/slick/Actor.scala | 16 ++++++++++++++++ .../co/absa/fadb/slick/ActorSlickConverter.scala | 16 ++++++++++++++++ .../slick/SlickMultipleResultFunctionTest.scala | 16 ++++++++++++++++ .../slick/SlickOptionalResultFunctionTest.scala | 16 ++++++++++++++++ .../scala/za/co/absa/fadb/slick/SlickTest.scala | 16 ++++++++++++++++ 14 files changed, 224 insertions(+) diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala index de6578d0..c1416317 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobie import cats.effect.unsafe.implicits.global diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala index 7a20205c..f44e03e1 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobie import cats.effect.unsafe.implicits.global diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala index 6ba9c4eb..950c5a2a 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobie import cats.effect.unsafe.implicits.global diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala index 0f047f84..4e00c793 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobie import cats.effect.unsafe.implicits.global diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala index 043001c8..625b08b9 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobie import cats.effect.IO diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index ed7b3130..50b3e441 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobie import cats.effect.IO diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala index 29603df8..60b6041e 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobie import cats.effect.IO diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala index a152c137..b9262148 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobie import doobie.util.Read diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/Results.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/Results.scala index 760c5e0d..734d7213 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/Results.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/Results.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobie import za.co.absa.fadb.status.FunctionStatus diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/Actor.scala b/slick/src/it/scala/za/co/absa/fadb/slick/Actor.scala index 54381be4..3aa92737 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/Actor.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/Actor.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.slick case class Actor(actorId: Int, firstName: String, lastName: String) diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/ActorSlickConverter.scala b/slick/src/it/scala/za/co/absa/fadb/slick/ActorSlickConverter.scala index 863cb189..bbf2defd 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/ActorSlickConverter.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/ActorSlickConverter.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.slick import slick.jdbc.{GetResult, PositionedResult} diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala index 68429128..a0c99432 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.slick import cats.implicits._ diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala index 1fc95d09..d5d7c746 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.slick import cats.implicits._ diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickTest.scala index 67e487e7..c9f8575b 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.slick import slick.jdbc.JdbcBackend.Database From 991b446478e83ac00fd4c7d6b9286ae4b8e2235b Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 5 Dec 2023 13:58:56 +0100 Subject: [PATCH 14/90] allow for other types for doobiepgengine --- .../DoobieMultipleResultFunctionTest.scala | 5 +- .../DoobieOptionalResultFunctionTest.scala | 5 +- .../DoobieSingleResultFunctionTest.scala | 5 +- ...eResultFunctionWithStatusSupportTest.scala | 5 +- .../co/absa/fadb/doobie/DoobieFunction.scala | 47 ++++++++++--------- .../co/absa/fadb/doobie/DoobiePgEngine.scala | 11 +++-- 6 files changed, 45 insertions(+), 33 deletions(-) diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala index c1416317..78efab9b 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala @@ -16,6 +16,7 @@ package za.co.absa.fadb.doobie +import cats.effect.IO import cats.effect.unsafe.implicits.global import doobie.implicits.toSqlInterpolator import doobie.util.Read @@ -26,8 +27,8 @@ import za.co.absa.fadb.doobie.DoobieFunction.DoobieMultipleResultFunction class DoobieMultipleResultFunctionTest extends AnyFunSuite with DoobieTest { - class GetActors(implicit schema: DBSchema, dbEngine: DoobiePgEngine) - extends DoobieMultipleResultFunction[GetActorsQueryParameters, Actor] { + class GetActors(implicit schema: DBSchema, dbEngine: DoobiePgEngine[IO]) + extends DoobieMultipleResultFunction[GetActorsQueryParameters, Actor, IO] { override def sql(values: GetActorsQueryParameters)(implicit read: Read[Actor]): Fragment = sql"SELECT actor_id, first_name, last_name FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala index f44e03e1..41e740cb 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala @@ -16,6 +16,7 @@ package za.co.absa.fadb.doobie +import cats.effect.IO import cats.effect.unsafe.implicits.global import doobie.implicits.toSqlInterpolator import doobie.util.Read @@ -26,8 +27,8 @@ import za.co.absa.fadb.doobie.DoobieFunction.DoobieOptionalResultFunction class DoobieOptionalResultFunctionTest extends AnyFunSuite with DoobieTest { - class GetActorById(implicit schema: DBSchema, dbEngine: DoobiePgEngine) - extends DoobieOptionalResultFunction[Int, Actor] { + class GetActorById(implicit schema: DBSchema, dbEngine: DoobiePgEngine[IO]) + extends DoobieOptionalResultFunction[Int, Actor, IO] { override def sql(values: Int)(implicit read: Read[Actor]): Fragment = sql"SELECT actor_id, first_name, last_name FROM ${Fragment.const(functionName)}($values)" diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala index 950c5a2a..cbfd676f 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala @@ -16,6 +16,7 @@ package za.co.absa.fadb.doobie +import cats.effect.IO import cats.effect.unsafe.implicits.global import doobie.implicits.toSqlInterpolator import doobie.util.Read @@ -26,8 +27,8 @@ import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunction class DoobieSingleResultFunctionTest extends AnyFunSuite with DoobieTest { - class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine) - extends DoobieSingleResultFunction[CreateActorRequestBody, Int] { + class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine[IO]) + extends DoobieSingleResultFunction[CreateActorRequestBody, Int, IO] { override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = sql"SELECT o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala index 4e00c793..145ed2d9 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala @@ -16,6 +16,7 @@ package za.co.absa.fadb.doobie +import cats.effect.IO import cats.effect.unsafe.implicits.global import doobie.Fragment import doobie.implicits.toSqlInterpolator @@ -28,8 +29,8 @@ import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling class DoobieSingleResultFunctionWithStatusSupportTest extends AnyFunSuite with DoobieTest { - class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine) - extends DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int] + class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine[IO]) + extends DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int, IO] with StandardStatusHandling { override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index 50b3e441..b107c2d8 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -16,7 +16,10 @@ package za.co.absa.fadb.doobie +import cats.Monad import cats.effect.IO +import cats.effect.kernel.Async +import cats.implicits.toFlatMapOps import doobie.util.Read import doobie.util.fragment.Fragment import za.co.absa.fadb.DBFunction._ @@ -25,6 +28,7 @@ import za.co.absa.fadb.doobie.Results.{FailedResult, ResultWithStatus, Successfu import za.co.absa.fadb.status.FunctionStatus import za.co.absa.fadb.status.handling.StatusHandling +import scala.language.higherKinds import scala.util.{Failure, Success} /** @@ -33,7 +37,7 @@ import scala.util.{Failure, Success} * @tparam I the input type of the function * @tparam R the result type of the function */ -private [doobie] trait DoobieFunction[I, R] { +private[doobie] trait DoobieFunction[I, R] { /** * The `Read[R]` instance used to read the query result into `R`. @@ -63,7 +67,7 @@ private [doobie] trait DoobieFunction[I, R] { * @tparam I the input type of the function * @tparam R the result type of the function */ -private [doobie] trait DoobieFunctionWithStatusSupport[I, R] extends StatusHandling { +private[doobie] trait DoobieFunctionWithStatusSupport[I, R] extends StatusHandling { /** * The `Read[R]` instance used to read the query result into `R`. @@ -107,11 +111,11 @@ object DoobieFunction { * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries * @param readR the `Read[R]` instance used to read the query result into `R` */ - abstract class DoobieSingleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit - override val schema: DBSchema, - val dbEngine: DoobiePgEngine, + abstract class DoobieSingleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( + implicit override val schema: DBSchema, + val dbEngine: DoobiePgEngine[F], val readR: Read[R] - ) extends DBSingleResultFunction[I, R, DoobiePgEngine, IO](functionNameOverride) + ) extends DBSingleResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) with DoobieFunction[I, R] {} /** @@ -123,11 +127,11 @@ object DoobieFunction { * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries * @param readR the `Read[R]` instance used to read the query result into `R` */ - abstract class DoobieMultipleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit - override val schema: DBSchema, - val dbEngine: DoobiePgEngine, + abstract class DoobieMultipleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( + implicit override val schema: DBSchema, + val dbEngine: DoobiePgEngine[F], val readR: Read[R] - ) extends DBMultipleResultFunction[I, R, DoobiePgEngine, IO](functionNameOverride) + ) extends DBMultipleResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) with DoobieFunction[I, R] {} /** @@ -139,11 +143,11 @@ object DoobieFunction { * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries * @param readR the `Read[R]` instance used to read the query result into `R` */ - abstract class DoobieOptionalResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit - override val schema: DBSchema, - val dbEngine: DoobiePgEngine, + abstract class DoobieOptionalResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( + implicit override val schema: DBSchema, + val dbEngine: DoobiePgEngine[F], val readR: Read[R] - ) extends DBOptionalResultFunction[I, R, DoobiePgEngine, IO](functionNameOverride) + ) extends DBOptionalResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) with DoobieFunction[I, R] {} /** @@ -156,12 +160,13 @@ object DoobieFunction { * @param readR the `Read[R]` instance used to read the query result into `R` * @param readSelectWithStatus the `Read[(Int, String, R)]` instance used to read the query result with status into `(Int, String, R)` */ - abstract class DoobieSingleResultFunctionWithStatusSupport[I, R](functionNameOverride: Option[String] = None)(implicit - override val schema: DBSchema, - val dbEngine: DoobiePgEngine, + abstract class DoobieSingleResultFunctionWithStatusSupport[I, R, F[_]: Async: Monad]( + functionNameOverride: Option[String] = None + )(implicit override val schema: DBSchema, + val dbEngine: DoobiePgEngine[F], val readR: Read[R], val readSelectWithStatus: Read[(Int, String, R)] - ) extends DBSingleResultFunction[I, (Int, String, R), DoobiePgEngine, IO](functionNameOverride) + ) extends DBSingleResultFunction[I, (Int, String, R), DoobiePgEngine[F], F](functionNameOverride) with DoobieFunctionWithStatusSupport[I, R] { /** @@ -170,11 +175,11 @@ object DoobieFunction { * @param values the input values for the function * @return the result with status */ - def applyWithStatus(values: I): IO[ResultWithStatus[R]] = { + def applyWithStatus(values: I)(implicit monad: Monad[F]): F[ResultWithStatus[R]] = { super.apply(values).flatMap { case (status, statusText, result) => checkStatus(status, statusText) match { - case Success(_) => IO.pure(Right(SuccessfulResult[R](FunctionStatus(status, statusText), result))) - case Failure(e) => IO.pure(Left(FailedResult(FunctionStatus(status, statusText), e))) + case Success(_) => monad.pure(Right(SuccessfulResult[R](FunctionStatus(status, statusText), result))) + case Failure(e) => monad.pure(Left(FailedResult(FunctionStatus(status, statusText), e))) } } } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala index 60b6041e..38375173 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala @@ -16,18 +16,21 @@ package za.co.absa.fadb.doobie -import cats.effect.IO +import cats.Monad +import cats.effect.Async import doobie._ import doobie.implicits._ import za.co.absa.fadb.DBEngine +import scala.language.higherKinds + /** * `DoobiePgEngine` is a class that extends `DBEngine` with `IO` as the effect type. * It uses Doobie's `Transactor[IO]` to execute SQL queries. * * @param transactor the Doobie transactor for executing SQL queries */ -class DoobiePgEngine(val transactor: Transactor[IO]) extends DBEngine[IO] { +class DoobiePgEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DBEngine[F] { /** The type of Doobie queries that produce `T` */ type QueryType[T] = DoobieQuery[T] @@ -39,7 +42,7 @@ class DoobiePgEngine(val transactor: Transactor[IO]) extends DBEngine[IO] { * @param readR the `Read[R]` instance used to read the query result into `R` * @return the query result as an `IO[Seq[R]]` */ - private def executeQuery[R](query: QueryType[R])(implicit readR: Read[R]): IO[Seq[R]] = { + private def executeQuery[R](query: QueryType[R])(implicit readR: Read[R]): F[Seq[R]] = { query.fragment.query[R].to[Seq].transact(transactor) } @@ -49,6 +52,6 @@ class DoobiePgEngine(val transactor: Transactor[IO]) extends DBEngine[IO] { * @param query the Doobie query to run * @return the query result as an `IO[Seq[R]]` */ - override def run[R](query: QueryType[R]): IO[Seq[R]] = + override def run[R](query: QueryType[R]): F[Seq[R]] = executeQuery(query)(query.readR) } From 8bd682e04c09541d75485c34eb16c53bd694664f Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 5 Dec 2023 14:12:30 +0100 Subject: [PATCH 15/90] scaladocs update with respect to generic type of doobiedbengine --- doobie/src/it/database/actors.ddl | 25 ++++++++++++---- doobie/src/it/database/create_actor.sql | 15 ++++++---- doobie/src/it/database/get_actor_by_id.sql | 19 ++++++------ doobie/src/it/database/get_actors.sql | 5 ++-- .../DoobieMultipleResultFunctionTest.scala | 2 +- .../DoobieOptionalResultFunctionTest.scala | 2 +- .../DoobieSingleResultFunctionTest.scala | 2 +- ...eResultFunctionWithStatusSupportTest.scala | 4 +-- .../co/absa/fadb/doobie/DoobieFunction.scala | 29 ++++++++++++------- .../co/absa/fadb/doobie/DoobiePgEngine.scala | 16 ++++++---- 10 files changed, 75 insertions(+), 44 deletions(-) diff --git a/doobie/src/it/database/actors.ddl b/doobie/src/it/database/actors.ddl index 5885bfb7..2d3f3ba9 100644 --- a/doobie/src/it/database/actors.ddl +++ b/doobie/src/it/database/actors.ddl @@ -1,5 +1,20 @@ -CREATE TABLE IF NOT EXISTS runs.actors ( - actor_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - first_name VARCHAR(150) NOT NULL, - last_name VARCHAR(150) NOT NULL -); \ No newline at end of file +CREATE TABLE IF NOT EXISTS runs.actors +( + actor_id + INT + GENERATED + BY + DEFAULT AS + IDENTITY + PRIMARY + KEY, + first_name + VARCHAR +( + 150 +) NOT NULL, + last_name VARCHAR +( + 150 +) NOT NULL + ); \ No newline at end of file diff --git a/doobie/src/it/database/create_actor.sql b/doobie/src/it/database/create_actor.sql index 77a3d1be..215e4c24 100644 --- a/doobie/src/it/database/create_actor.sql +++ b/doobie/src/it/database/create_actor.sql @@ -15,7 +15,8 @@ -- Example: -- SELECT * FROM runs.create_actor('John', 'Doe'); -CREATE OR REPLACE FUNCTION runs.create_actor( +CREATE +OR REPLACE FUNCTION runs.create_actor( IN i_first_name TEXT, IN i_last_name TEXT, OUT status INTEGER, @@ -24,12 +25,14 @@ CREATE OR REPLACE FUNCTION runs.create_actor( ) RETURNS record AS $$ BEGIN - INSERT INTO runs.actors(first_name, last_name) - VALUES (i_first_name, i_last_name) - RETURNING actor_id INTO o_actor_id; +INSERT INTO runs.actors(first_name, last_name) +VALUES (i_first_name, i_last_name) RETURNING actor_id +INTO o_actor_id; - status := 11; - status_text := 'Actor created'; +status +:= 11; + status_text +:= 'Actor created'; RETURN; END; diff --git a/doobie/src/it/database/get_actor_by_id.sql b/doobie/src/it/database/get_actor_by_id.sql index 96f70006..e6e64df9 100644 --- a/doobie/src/it/database/get_actor_by_id.sql +++ b/doobie/src/it/database/get_actor_by_id.sql @@ -1,24 +1,25 @@ /* * Function: runs.get_actor_by_id - * - * Description: + * + * Description: * This function retrieves an actor from the 'runs.actors' table by their ID. - * + * * Parameters: * i_actor_id - The ID of the actor to retrieve. - * - * Returns: + * + * Returns: * A table with the following columns: * actor_id - The ID of the actor. * first_name - The first name of the actor. * last_name - The last name of the actor. - * + * * Example: * SELECT * FROM runs.get_actor_by_id(1); - * + * * This will return the actor with ID 1, if he/she exists. */ -CREATE OR REPLACE FUNCTION runs.get_actor_by_id( +CREATE +OR REPLACE FUNCTION runs.get_actor_by_id( i_actor_id INTEGER ) RETURNS TABLE ( actor_id INTEGER, @@ -27,7 +28,7 @@ CREATE OR REPLACE FUNCTION runs.get_actor_by_id( ) AS $$ BEGIN - RETURN QUERY SELECT A.actor_id, A.first_name, A.last_name +RETURN QUERY SELECT A.actor_id, A.first_name, A.last_name FROM runs.actors A WHERE A.actor_id = i_actor_id; END; diff --git a/doobie/src/it/database/get_actors.sql b/doobie/src/it/database/get_actors.sql index f777464b..46b9bfaf 100644 --- a/doobie/src/it/database/get_actors.sql +++ b/doobie/src/it/database/get_actors.sql @@ -1,4 +1,5 @@ -CREATE OR REPLACE FUNCTION runs.get_actors( +CREATE +OR REPLACE FUNCTION runs.get_actors( i_first_name TEXT, i_last_name TEXT ) RETURNS TABLE ( @@ -8,7 +9,7 @@ CREATE OR REPLACE FUNCTION runs.get_actors( ) AS $$ BEGIN - RETURN QUERY SELECT A.actor_id, A.first_name, A.last_name +RETURN QUERY SELECT A.actor_id, A.first_name, A.last_name FROM runs.actors A WHERE (i_first_name IS NULL OR A.first_name = i_first_name) diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala index 78efab9b..53bae1bc 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala @@ -28,7 +28,7 @@ import za.co.absa.fadb.doobie.DoobieFunction.DoobieMultipleResultFunction class DoobieMultipleResultFunctionTest extends AnyFunSuite with DoobieTest { class GetActors(implicit schema: DBSchema, dbEngine: DoobiePgEngine[IO]) - extends DoobieMultipleResultFunction[GetActorsQueryParameters, Actor, IO] { + extends DoobieMultipleResultFunction[GetActorsQueryParameters, Actor, IO] { override def sql(values: GetActorsQueryParameters)(implicit read: Read[Actor]): Fragment = sql"SELECT actor_id, first_name, last_name FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala index 41e740cb..cb803704 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala @@ -28,7 +28,7 @@ import za.co.absa.fadb.doobie.DoobieFunction.DoobieOptionalResultFunction class DoobieOptionalResultFunctionTest extends AnyFunSuite with DoobieTest { class GetActorById(implicit schema: DBSchema, dbEngine: DoobiePgEngine[IO]) - extends DoobieOptionalResultFunction[Int, Actor, IO] { + extends DoobieOptionalResultFunction[Int, Actor, IO] { override def sql(values: Int)(implicit read: Read[Actor]): Fragment = sql"SELECT actor_id, first_name, last_name FROM ${Fragment.const(functionName)}($values)" diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala index cbfd676f..1aa2e61c 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala @@ -28,7 +28,7 @@ import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunction class DoobieSingleResultFunctionTest extends AnyFunSuite with DoobieTest { class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine[IO]) - extends DoobieSingleResultFunction[CreateActorRequestBody, Int, IO] { + extends DoobieSingleResultFunction[CreateActorRequestBody, Int, IO] { override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = sql"SELECT o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala index 145ed2d9..c9e78bc2 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala @@ -30,8 +30,8 @@ import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling class DoobieSingleResultFunctionWithStatusSupportTest extends AnyFunSuite with DoobieTest { class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine[IO]) - extends DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int, IO] - with StandardStatusHandling { + extends DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int, IO] + with StandardStatusHandling { override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index b107c2d8..ecf1917b 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -103,66 +103,74 @@ private[doobie] trait DoobieFunctionWithStatusSupport[I, R] extends StatusHandli object DoobieFunction { /** - * `DoobieSingleResultFunction` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type and `IO` as the effect type. + * `DoobieSingleResultFunction` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type. * It represents a database function that returns a single result. * * @param functionNameOverride the optional override for the function name * @param schema the database schema * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries * @param readR the `Read[R]` instance used to read the query result into `R` + * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieSingleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, + implicit + override val schema: DBSchema, val dbEngine: DoobiePgEngine[F], val readR: Read[R] ) extends DBSingleResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) with DoobieFunction[I, R] {} /** - * `DoobieMultipleResultFunction` is an abstract class that extends `DBMultipleResultFunction` with `DoobiePgEngine` as the engine type and `IO` as the effect type. + * `DoobieMultipleResultFunction` is an abstract class that extends `DBMultipleResultFunction` with `DoobiePgEngine` as the engine type. * It represents a database function that returns multiple results. * * @param functionNameOverride the optional override for the function name * @param schema the database schema * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries * @param readR the `Read[R]` instance used to read the query result into `R` + * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieMultipleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, + implicit + override val schema: DBSchema, val dbEngine: DoobiePgEngine[F], val readR: Read[R] ) extends DBMultipleResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) with DoobieFunction[I, R] {} /** - * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` with `DoobiePgEngine` as the engine type and `IO` as the effect type. + * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` with `DoobiePgEngine` as the engine type. * It represents a database function that returns an optional result. * * @param functionNameOverride the optional override for the function name * @param schema the database schema * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries * @param readR the `Read[R]` instance used to read the query result into `R` + * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieOptionalResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, + implicit + override val schema: DBSchema, val dbEngine: DoobiePgEngine[F], val readR: Read[R] ) extends DBOptionalResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) with DoobieFunction[I, R] {} /** - * `DoobieSingleResultFunctionWithStatusSupport` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type and `IO` as the effect type. - * It represents a database function that returns a single result and supports function status. + * `DoobieSingleResultFunctionWithStatusSupport` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type. + * It represents a database function that returns a single result with status. * * @param functionNameOverride the optional override for the function name * @param schema the database schema * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries * @param readR the `Read[R]` instance used to read the query result into `R` * @param readSelectWithStatus the `Read[(Int, String, R)]` instance used to read the query result with status into `(Int, String, R)` + * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieSingleResultFunctionWithStatusSupport[I, R, F[_]: Async: Monad]( functionNameOverride: Option[String] = None - )(implicit override val schema: DBSchema, + )(implicit + override val schema: DBSchema, val dbEngine: DoobiePgEngine[F], val readR: Read[R], val readSelectWithStatus: Read[(Int, String, R)] @@ -173,6 +181,7 @@ object DoobieFunction { * Executes the function with the given input values and returns the result with status. * * @param values the input values for the function + * @param monad the `Monad` instance used to chain operations together * @return the result with status */ def applyWithStatus(values: I)(implicit monad: Monad[F]): F[ResultWithStatus[R]] = { @@ -185,6 +194,4 @@ object DoobieFunction { } } - // for an example see DoobieFunctionWithStatusSupportTest - } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala index 38375173..d40f7202 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala @@ -25,10 +25,14 @@ import za.co.absa.fadb.DBEngine import scala.language.higherKinds /** - * `DoobiePgEngine` is a class that extends `DBEngine` with `IO` as the effect type. - * It uses Doobie's `Transactor[IO]` to execute SQL queries. + * `DoobiePgEngine` is a class that extends `DBEngine` with `F` as the effect type. + * It uses Doobie's `Transactor[F]` to execute SQL queries. + * + * `Async` is needed because Doobie requires it for non-blocking database operations. + * `Monad` is needed because it provides an interface for chaining operations together. * * @param transactor the Doobie transactor for executing SQL queries + * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ class DoobiePgEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DBEngine[F] { @@ -36,21 +40,21 @@ class DoobiePgEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends type QueryType[T] = DoobieQuery[T] /** - * Executes a Doobie query and returns the result as an `IO[Seq[R]]`. + * Executes a Doobie query and returns the result as an `F[Seq[R]]`. * * @param query the Doobie query to execute * @param readR the `Read[R]` instance used to read the query result into `R` - * @return the query result as an `IO[Seq[R]]` + * @return the query result as an `F[Seq[R]]` */ private def executeQuery[R](query: QueryType[R])(implicit readR: Read[R]): F[Seq[R]] = { query.fragment.query[R].to[Seq].transact(transactor) } /** - * Runs a Doobie query and returns the result as an `IO[Seq[R]]`. + * Runs a Doobie query and returns the result as an `F[Seq[R]]`. * * @param query the Doobie query to run - * @return the query result as an `IO[Seq[R]]` + * @return the query result as an `F[Seq[R]]` */ override def run[R](query: QueryType[R]): F[Seq[R]] = executeQuery(query)(query.readR) From 25c42ec0a25e23ef2121923924c380f0f55de2b8 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 5 Dec 2023 14:15:39 +0100 Subject: [PATCH 16/90] fix formatting --- doobie/src/it/database/actors.ddl | 24 +----- .../co/absa/fadb/doobie/DoobieFunction.scala | 86 +++++++++---------- 2 files changed, 45 insertions(+), 65 deletions(-) diff --git a/doobie/src/it/database/actors.ddl b/doobie/src/it/database/actors.ddl index 2d3f3ba9..7130616c 100644 --- a/doobie/src/it/database/actors.ddl +++ b/doobie/src/it/database/actors.ddl @@ -1,20 +1,4 @@ -CREATE TABLE IF NOT EXISTS runs.actors -( - actor_id - INT - GENERATED - BY - DEFAULT AS - IDENTITY - PRIMARY - KEY, - first_name - VARCHAR -( - 150 -) NOT NULL, - last_name VARCHAR -( - 150 -) NOT NULL - ); \ No newline at end of file +CREATE TABLE IF NOT EXISTS runs.actors( + actor_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + first_name VARCHAR(150) NOT NULL, + last_name VARCHAR(150) NOT NULL); \ No newline at end of file diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index ecf1917b..f64c597d 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -103,54 +103,51 @@ private[doobie] trait DoobieFunctionWithStatusSupport[I, R] extends StatusHandli object DoobieFunction { /** - * `DoobieSingleResultFunction` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type. - * It represents a database function that returns a single result. - * - * @param functionNameOverride the optional override for the function name - * @param schema the database schema - * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries - * @param readR the `Read[R]` instance used to read the query result into `R` - * @tparam F the effect type, which must have an `Async` and a `Monad` instance + * `DoobieSingleResultFunction` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type. + * It represents a database function that returns a single result. + * + * @param functionNameOverride the optional override for the function name + * @param schema the database schema + * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param readR the `Read[R]` instance used to read the query result into `R` + * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieSingleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit - override val schema: DBSchema, + implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine[F], val readR: Read[R] ) extends DBSingleResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) with DoobieFunction[I, R] {} /** - * `DoobieMultipleResultFunction` is an abstract class that extends `DBMultipleResultFunction` with `DoobiePgEngine` as the engine type. - * It represents a database function that returns multiple results. - * - * @param functionNameOverride the optional override for the function name - * @param schema the database schema - * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries - * @param readR the `Read[R]` instance used to read the query result into `R` - * @tparam F the effect type, which must have an `Async` and a `Monad` instance + * `DoobieMultipleResultFunction` is an abstract class that extends `DBMultipleResultFunction` with `DoobiePgEngine` as the engine type. + * It represents a database function that returns multiple results. + * + * @param functionNameOverride the optional override for the function name + * @param schema the database schema + * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param readR the `Read[R]` instance used to read the query result into `R` + * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieMultipleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit - override val schema: DBSchema, + implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine[F], val readR: Read[R] ) extends DBMultipleResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) with DoobieFunction[I, R] {} /** - * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` with `DoobiePgEngine` as the engine type. - * It represents a database function that returns an optional result. - * - * @param functionNameOverride the optional override for the function name - * @param schema the database schema - * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries - * @param readR the `Read[R]` instance used to read the query result into `R` - * @tparam F the effect type, which must have an `Async` and a `Monad` instance + * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` with `DoobiePgEngine` as the engine type. + * It represents a database function that returns an optional result. + * + * @param functionNameOverride the optional override for the function name + * @param schema the database schema + * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param readR the `Read[R]` instance used to read the query result into `R` + * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieOptionalResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit - override val schema: DBSchema, + implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine[F], val readR: Read[R] ) extends DBOptionalResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) @@ -158,19 +155,18 @@ object DoobieFunction { /** * `DoobieSingleResultFunctionWithStatusSupport` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type. - * It represents a database function that returns a single result with status. + * It represents a database function that returns a single result with status. * - * @param functionNameOverride the optional override for the function name - * @param schema the database schema - * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries - * @param readR the `Read[R]` instance used to read the query result into `R` - * @param readSelectWithStatus the `Read[(Int, String, R)]` instance used to read the query result with status into `(Int, String, R)` - * @tparam F the effect type, which must have an `Async` and a `Monad` instance + * @param functionNameOverride the optional override for the function name + * @param schema the database schema + * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param readR the `Read[R]` instance used to read the query result into `R` + * @param readSelectWithStatus the `Read[(Int, String, R)]` instance used to read the query result with status into `(Int, String, R)` + * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieSingleResultFunctionWithStatusSupport[I, R, F[_]: Async: Monad]( functionNameOverride: Option[String] = None - )(implicit - override val schema: DBSchema, + )(implicit override val schema: DBSchema, val dbEngine: DoobiePgEngine[F], val readR: Read[R], val readSelectWithStatus: Read[(Int, String, R)] @@ -178,12 +174,12 @@ object DoobieFunction { with DoobieFunctionWithStatusSupport[I, R] { /** - * Executes the function with the given input values and returns the result with status. - * - * @param values the input values for the function - * @param monad the `Monad` instance used to chain operations together - * @return the result with status - */ + * Executes the function with the given input values and returns the result with status. + * + * @param values the input values for the function + * @param monad the `Monad` instance used to chain operations together + * @return the result with status + */ def applyWithStatus(values: I)(implicit monad: Monad[F]): F[ResultWithStatus[R]] = { super.apply(values).flatMap { case (status, statusText, result) => checkStatus(status, statusText) match { From 1b789bb1032b6ffe2030b7faa8ef9044c52720b8 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 5 Dec 2023 14:46:11 +0100 Subject: [PATCH 17/90] fix slick part and added cats effect dependency --- .../src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala | 1 - project/Dependencies.scala | 3 ++- .../co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala | 1 - .../co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala | 2 -- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index f64c597d..7b77aff8 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -17,7 +17,6 @@ package za.co.absa.fadb.doobie import cats.Monad -import cats.effect.IO import cats.effect.kernel.Async import cats.implicits.toFlatMapOps import doobie.util.Read diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 7d10b97f..b7c8b17b 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -19,7 +19,8 @@ import sbt._ object Dependencies { private def commonDependencies(scalaVersion: String): Seq[ModuleID] = Seq( - "org.typelevel" %% "cats-core" % "2.0.0", // 2.0.0 latest for Scala 2.11, 2.9.0 used by Doobie + "org.typelevel" %% "cats-core" % "2.9.0", // 2.0.0 latest for Scala 2.11, 2.9.0 used by Doobie + "org.typelevel" %% "cats-effect" % "3.5.0", // 2.0.0 latest for Scala 2.11, 2.9.0 used by Doobie "org.scalatest" %% "scalatest" % "3.1.0" % "test,it", "org.scalatest" %% "scalatest-flatspec" % "3.2.0" % "test,it", "org.scalatestplus" %% "mockito-1-10" % "3.1.0.0" % "test,it" diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala index a0c99432..c1682678 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala @@ -16,7 +16,6 @@ package za.co.absa.fadb.slick -import cats.implicits._ import org.scalatest.funsuite.AnyFunSuite import slick.jdbc.SQLActionBuilder import za.co.absa.fadb.DBFunction.DBMultipleResultFunction diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala index d5d7c746..be56affd 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala @@ -16,8 +16,6 @@ package za.co.absa.fadb.slick -import cats.implicits._ - import scala.concurrent.ExecutionContext.Implicits.global import org.scalatest.funsuite.AnyFunSuite import slick.jdbc.SQLActionBuilder From a95d95893bdfd6eb9006dc22093bbb4a1e34b3c0 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 5 Dec 2023 14:48:20 +0100 Subject: [PATCH 18/90] minors --- .../main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index 7b77aff8..e9b4fcf5 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -116,7 +116,7 @@ object DoobieFunction { val dbEngine: DoobiePgEngine[F], val readR: Read[R] ) extends DBSingleResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) - with DoobieFunction[I, R] {} + with DoobieFunction[I, R] /** * `DoobieMultipleResultFunction` is an abstract class that extends `DBMultipleResultFunction` with `DoobiePgEngine` as the engine type. @@ -133,7 +133,7 @@ object DoobieFunction { val dbEngine: DoobiePgEngine[F], val readR: Read[R] ) extends DBMultipleResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) - with DoobieFunction[I, R] {} + with DoobieFunction[I, R] /** * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` with `DoobiePgEngine` as the engine type. @@ -150,7 +150,7 @@ object DoobieFunction { val dbEngine: DoobiePgEngine[F], val readR: Read[R] ) extends DBOptionalResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) - with DoobieFunction[I, R] {} + with DoobieFunction[I, R] /** * `DoobieSingleResultFunctionWithStatusSupport` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type. From f49645ca35d0686587e6dc6d6593e545c42a4dc8 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 5 Dec 2023 15:26:38 +0100 Subject: [PATCH 19/90] relax monad requirement from dobiepgengine --- .../src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala index d40f7202..77d6582d 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala @@ -16,7 +16,6 @@ package za.co.absa.fadb.doobie -import cats.Monad import cats.effect.Async import doobie._ import doobie.implicits._ @@ -34,7 +33,7 @@ import scala.language.higherKinds * @param transactor the Doobie transactor for executing SQL queries * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ -class DoobiePgEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DBEngine[F] { +class DoobiePgEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[F] { /** The type of Doobie queries that produce `T` */ type QueryType[T] = DoobieQuery[T] From b76801bdf4d776b0a794b880cd069231eb2cdda6 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 5 Dec 2023 19:10:33 +0100 Subject: [PATCH 20/90] minors --- .../DoobieMultipleResultFunctionTest.scala | 4 ++-- .../DoobieOptionalResultFunctionTest.scala | 4 ++-- .../doobie/DoobieSingleResultFunctionTest.scala | 4 ++-- ...ngleResultFunctionWithStatusSupportTest.scala | 4 ++-- .../{DoobiePgEngine.scala => DoobieEngine.scala} | 3 +-- .../za/co/absa/fadb/doobie/DoobieFunction.scala | 16 ++++++++-------- 6 files changed, 17 insertions(+), 18 deletions(-) rename doobie/src/main/scala/za/co/absa/fadb/doobie/{DoobiePgEngine.scala => DoobieEngine.scala} (91%) diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala index 53bae1bc..f0323196 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala @@ -27,14 +27,14 @@ import za.co.absa.fadb.doobie.DoobieFunction.DoobieMultipleResultFunction class DoobieMultipleResultFunctionTest extends AnyFunSuite with DoobieTest { - class GetActors(implicit schema: DBSchema, dbEngine: DoobiePgEngine[IO]) + class GetActors(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) extends DoobieMultipleResultFunction[GetActorsQueryParameters, Actor, IO] { override def sql(values: GetActorsQueryParameters)(implicit read: Read[Actor]): Fragment = sql"SELECT actor_id, first_name, last_name FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" } - private val getActors = new GetActors()(Runs, new DoobiePgEngine(transactor)) + private val getActors = new GetActors()(Runs, new DoobieEngine(transactor)) test("DoobieTest") { val expectedResultElem = Actor(49, "Pavel", "Marek") diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala index cb803704..0178b7a5 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala @@ -27,14 +27,14 @@ import za.co.absa.fadb.doobie.DoobieFunction.DoobieOptionalResultFunction class DoobieOptionalResultFunctionTest extends AnyFunSuite with DoobieTest { - class GetActorById(implicit schema: DBSchema, dbEngine: DoobiePgEngine[IO]) + class GetActorById(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) extends DoobieOptionalResultFunction[Int, Actor, IO] { override def sql(values: Int)(implicit read: Read[Actor]): Fragment = sql"SELECT actor_id, first_name, last_name FROM ${Fragment.const(functionName)}($values)" } - private val createActor = new GetActorById()(Runs, new DoobiePgEngine(transactor)) + private val createActor = new GetActorById()(Runs, new DoobieEngine(transactor)) test("DoobieTest") { val expectedResult = Some(Actor(49, "Pavel", "Marek")) diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala index 1aa2e61c..6a9f3615 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala @@ -27,14 +27,14 @@ import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunction class DoobieSingleResultFunctionTest extends AnyFunSuite with DoobieTest { - class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine[IO]) + class CreateActor(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) extends DoobieSingleResultFunction[CreateActorRequestBody, Int, IO] { override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = sql"SELECT o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" } - private val createActor = new CreateActor()(Runs, new DoobiePgEngine(transactor)) + private val createActor = new CreateActor()(Runs, new DoobieEngine(transactor)) test("DoobieTest") { assert(createActor(CreateActorRequestBody("Pavel", "Marek")).unsafeRunSync().isInstanceOf[Int]) diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala index c9e78bc2..d8c2f7a5 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala @@ -29,7 +29,7 @@ import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling class DoobieSingleResultFunctionWithStatusSupportTest extends AnyFunSuite with DoobieTest { - class CreateActor(implicit schema: DBSchema, dbEngine: DoobiePgEngine[IO]) + class CreateActor(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) extends DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int, IO] with StandardStatusHandling { @@ -37,7 +37,7 @@ class DoobieSingleResultFunctionWithStatusSupportTest extends AnyFunSuite with D sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" } - private val createActor = new CreateActor()(Runs, new DoobiePgEngine(transactor)) + private val createActor = new CreateActor()(Runs, new DoobieEngine(transactor)) test("DoobieTest with status handling") { val requestBody = CreateActorRequestBody("Pavel", "Marek") diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala similarity index 91% rename from doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala rename to doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala index 77d6582d..0df23dcd 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobiePgEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala @@ -28,12 +28,11 @@ import scala.language.higherKinds * It uses Doobie's `Transactor[F]` to execute SQL queries. * * `Async` is needed because Doobie requires it for non-blocking database operations. - * `Monad` is needed because it provides an interface for chaining operations together. * * @param transactor the Doobie transactor for executing SQL queries * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ -class DoobiePgEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[F] { +class DoobieEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[F] { /** The type of Doobie queries that produce `T` */ type QueryType[T] = DoobieQuery[T] diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index e9b4fcf5..fdce82ca 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -113,9 +113,9 @@ object DoobieFunction { */ abstract class DoobieSingleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, - val dbEngine: DoobiePgEngine[F], + val dbEngine: DoobieEngine[F], val readR: Read[R] - ) extends DBSingleResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) + ) extends DBSingleResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) with DoobieFunction[I, R] /** @@ -130,9 +130,9 @@ object DoobieFunction { */ abstract class DoobieMultipleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, - val dbEngine: DoobiePgEngine[F], + val dbEngine: DoobieEngine[F], val readR: Read[R] - ) extends DBMultipleResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) + ) extends DBMultipleResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) with DoobieFunction[I, R] /** @@ -147,9 +147,9 @@ object DoobieFunction { */ abstract class DoobieOptionalResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, - val dbEngine: DoobiePgEngine[F], + val dbEngine: DoobieEngine[F], val readR: Read[R] - ) extends DBOptionalResultFunction[I, R, DoobiePgEngine[F], F](functionNameOverride) + ) extends DBOptionalResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) with DoobieFunction[I, R] /** @@ -166,10 +166,10 @@ object DoobieFunction { abstract class DoobieSingleResultFunctionWithStatusSupport[I, R, F[_]: Async: Monad]( functionNameOverride: Option[String] = None )(implicit override val schema: DBSchema, - val dbEngine: DoobiePgEngine[F], + val dbEngine: DoobieEngine[F], val readR: Read[R], val readSelectWithStatus: Read[(Int, String, R)] - ) extends DBSingleResultFunction[I, (Int, String, R), DoobiePgEngine[F], F](functionNameOverride) + ) extends DBSingleResultFunction[I, (Int, String, R), DoobieEngine[F], F](functionNameOverride) with DoobieFunctionWithStatusSupport[I, R] { /** From d75b13a1fb66b2fb9060e0eef061cb0c787eccb9 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 7 Dec 2023 17:57:50 +0100 Subject: [PATCH 21/90] tmp commit --- .../main/scala/za/co/absa/fadb/DBEngine.scala | 8 +- .../scala/za/co/absa/fadb/DBFunction.scala | 65 ++++-- .../za/co/absa/fadb/DBFunctionFabric.scala | 25 ++- .../co/absa/fadb/FunctionStatusWithData.scala | 5 + .../main/scala/za/co/absa/fadb/Query.scala | 14 +- .../fadb/exceptions/DBFailException.scala | 34 --- .../co/absa/fadb/status/StatusException.scala | 65 +----- .../status/handling/QueryStatusHandling.scala | 23 ++ .../StandardStatusHandling.scala | 41 ---- .../UserDefinedStatusHandling.scala | 37 ---- .../za/co/absa/fadb/DBFunctionSuite.scala | 138 ++++++------ .../fadb/status/StatusExceptionSuite.scala | 104 ++++----- .../fadb/status/StatusHandlingSuite.scala | 86 ++++---- .../UserDefinedStatusHandlingSuite.scala | 43 ---- .../StandardStatusHandlingSuite.scala | 64 ------ ...nctionWithStandardStatusHandlingTest.scala | 26 +++ ...eResultFunctionWithStatusSupportTest.scala | 101 +++++---- .../za/co/absa/fadb/doobie/DoobieEngine.scala | 21 +- .../co/absa/fadb/doobie/DoobieFunction.scala | 200 ++++++++++-------- .../za/co/absa/fadb/doobie/DoobieQuery.scala | 22 +- .../examples/enceladus/DatasetSchema.scala | 1 - .../za/co/absa/fadb/slick/SlickPgEngine.scala | 35 +-- .../za/co/absa/fadb/slick/SlickQuery.scala | 35 ++- 23 files changed, 560 insertions(+), 633 deletions(-) create mode 100644 core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala delete mode 100644 core/src/main/scala/za/co/absa/fadb/exceptions/DBFailException.scala create mode 100644 core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala delete mode 100644 core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala delete mode 100644 core/src/main/scala/za/co/absa/fadb/status/handling/implementations/UserDefinedStatusHandling.scala delete mode 100644 core/src/test/scala/za/co/absa/fadb/status/UserDefinedStatusHandlingSuite.scala delete mode 100644 core/src/test/scala/za/co/absa/fadb/status/implementations/StandardStatusHandlingSuite.scala create mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStandardStatusHandlingTest.scala diff --git a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala index 06b75cab..882b5fe2 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala @@ -18,6 +18,7 @@ package za.co.absa.fadb import cats.Monad import cats.implicits.toFunctorOps +import za.co.absa.fadb.status.StatusException import scala.language.higherKinds @@ -31,6 +32,8 @@ abstract class DBEngine[F[_]: Monad] { * @tparam T - the return type of the query */ type QueryType[T] <: Query[T] +// type QueryWithStatusType[A, B, R] <: QueryWithStatus[A, B, R] + type QueryWithStatusType[R] <: QueryWithStatus[_, _, R] /** * The actual query executioner of the queries of the engine @@ -40,6 +43,9 @@ abstract class DBEngine[F[_]: Monad] { */ protected def run[R](query: QueryType[R]): F[Seq[R]] +// def fetchHeadWithStatusHandling[A, B, R](query: QueryWithStatusType[A, B, R]): F[Either[StatusException, R]] + def fetchHeadWithStatusHandling[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] + /** * Public method to execute when query is expected to return multiple results * @param query - the query to execute @@ -65,7 +71,7 @@ abstract class DBEngine[F[_]: Monad] { * @return - sequence of the results of database query */ - def fetchHeadOption[R](query: QueryType[R]): F[Option[R]] = { + def fetchHeadOption[R](query: QueryType[R]): F[Option[R]] = { run(query).map(_.headOption) } } diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index fbc71a06..299b5d64 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -18,6 +18,7 @@ package za.co.absa.fadb import cats.Monad import za.co.absa.fadb.naming.NamingConvention +import za.co.absa.fadb.status.StatusException import scala.language.higherKinds @@ -33,7 +34,8 @@ import scala.language.higherKinds * @tparam E - the type of the [[DBEngine]] engine */ abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) - (implicit val schema: DBSchema, val dBEngine: E) extends DBFunctionFabric { + (implicit override val schema: DBSchema, val dBEngine: E) + extends DBFunctionFabric(functionNameOverride) { // A constructor that takes only the mandatory parameters and uses default values for the optional ones def this()(implicit schema: DBSchema, dBEngine: E) = this(None) @@ -49,31 +51,33 @@ abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverr */ protected def query(values: I): dBEngine.QueryType[R] - /** - * Name of the function, based on the class name, unless it is overridden in the constructor - */ - val functionName: String = { - val fn = functionNameOverride.getOrElse(schema.objectNameFromClassName(getClass)) - if (schema.schemaName.isEmpty) { - fn - } else { - s"${schema.schemaName}.$fn" - } - } - - def namingConvention: NamingConvention = schema.namingConvention - - /** - * List of fields to select from the DB function. Expected to be based on the return type `R` - * @return - list of fields to select - */ - override protected def fieldsToSelect: Seq[String] = super.fieldsToSelect //TODO should get the names from R #6 - /*these 3 functions has to be defined here and not in the ancestors, as there the query type is not compatible - path-dependent types*/ protected def multipleResults(values: I): F[Seq[R]] = dBEngine.fetchAll(query(values)) protected def singleResult(values: I): F[R] = dBEngine.fetchHead(query(values)) protected def optionalResult(values: I): F[Option[R]] = dBEngine.fetchHeadOption(query(values)) +} + +abstract class DBFunctionWithStatusHandling[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) + (implicit override val schema: DBSchema, val dBEngine: E) + extends DBFunctionFabric(functionNameOverride) { + // A constructor that takes only the mandatory parameters and uses default values for the optional ones + def this()(implicit schema: DBSchema, dBEngine: E) = this(None) + + // A constructor that allows specifying the function name as a string, but not as an option + def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) + + /** + * Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the + * DBEngine specific mix-in. + * @param values - the values to pass over to the database function + * @return - the SQL query in the format specific to the provided [[DBEngine]] + */ + protected def query(values: I): dBEngine.QueryWithStatusType[R] + + protected def singleResultWithStatusHandling(values: I): F[Either[StatusException, R]] = { + dBEngine.fetchHeadWithStatusHandling(query(values)) + } } object DBFunction { @@ -164,4 +168,23 @@ object DBFunction { */ def apply(values: I): F[Option[R]] = optionalResult(values) } + +// abstract class DBSingleResultWithStatusHandlingFunction[I, R, E <: DBEngine[F], F[_] : Monad](functionNameOverride: Option[String] = None) +// (implicit schema: DBSchema, dBEngine: E) +// extends DBFunctionWithStatusHandling[I, R, E, F](functionNameOverride) { +// +// // A constructor that takes only the mandatory parameters and uses default values for the optional ones +// def this()(implicit schema: DBSchema, dBEngine: E) = this(None) +// +// // A constructor that allows specifying the function name as a string, but not as an option +// def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) +// +// /** +// * For easy and convenient execution of the DB function call +// * +// * @param values - the values to pass over to the database function +// * @return - the value returned from the DB function transformed to scala type `R` +// */ +// def apply(values: I): F[Either[StatusException, R]] = singleResultWithStatusHandling(values) +// } } diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala b/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala index 5d4fc55e..120577dd 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala @@ -16,20 +16,31 @@ package za.co.absa.fadb +import za.co.absa.fadb.naming.NamingConvention + /** * This trait serves the purpose of introducing functions that are common to all DB Function objects and mix-in traits * that offer certain implementations. This trait should help with the inheritance of all of these */ -trait DBFunctionFabric { - - /** - * Name of the function the class represents - */ - def functionName: String +abstract class DBFunctionFabric(functionNameOverride: Option[String])(implicit val schema: DBSchema) { - /** + /** * List of fields to select from the DB function. * @return - list of fields to select */ protected def fieldsToSelect: Seq[String] = Seq.empty + + /** + * Name of the function, based on the class name, unless it is overridden in the constructor + */ + val functionName: String = { + val fn = functionNameOverride.getOrElse(schema.objectNameFromClassName(getClass)) + if (schema.schemaName.isEmpty) { + fn + } else { + s"${schema.schemaName}.$fn" + } + } + + def namingConvention: NamingConvention = schema.namingConvention } diff --git a/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala b/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala new file mode 100644 index 00000000..cb53b44b --- /dev/null +++ b/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala @@ -0,0 +1,5 @@ +package za.co.absa.fadb + +import za.co.absa.fadb.status.FunctionStatus + +case class FunctionStatusWithData[A] (functionStatus: FunctionStatus, data: A) diff --git a/core/src/main/scala/za/co/absa/fadb/Query.scala b/core/src/main/scala/za/co/absa/fadb/Query.scala index 79fdb6a4..54243359 100644 --- a/core/src/main/scala/za/co/absa/fadb/Query.scala +++ b/core/src/main/scala/za/co/absa/fadb/Query.scala @@ -16,8 +16,20 @@ package za.co.absa.fadb +import za.co.absa.fadb.status.handling.QueryStatusHandling +import za.co.absa.fadb.status.{FunctionStatus, StatusException} + /** * The basis for all query types of [[DBEngine]] implementations - * @tparam R - the return type of the query + * + * @tparam R - the return type of the query */ trait Query[R] + +trait QueryWithStatus[A, B, R] extends QueryStatusHandling { // A initial result, B after status extraction, R final result +//trait QueryWithStatus[R] extends QueryStatusHandling { // A initial result, B after status extraction, R final result +// def processStatus(initialResult: A): FunctionStatusWithData[B] + def processStatus(initialResult: A): FunctionStatusWithData[B] + def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[B]): Either[StatusException, R] // think of typ alias for Either[StatusException, R] + def getResultOrException(initialResult: A): Either[StatusException, R] = toStatusExceptionOrData(processStatus(initialResult)) +} diff --git a/core/src/main/scala/za/co/absa/fadb/exceptions/DBFailException.scala b/core/src/main/scala/za/co/absa/fadb/exceptions/DBFailException.scala deleted file mode 100644 index 6f693143..00000000 --- a/core/src/main/scala/za/co/absa/fadb/exceptions/DBFailException.scala +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb.exceptions - -/** - * General Fa-DB exception class - * @param message - the message describing the reason of exception - */ -class DBFailException(message: String) extends Exception(message) { - override def equals(obj: Any): Boolean = { - obj match { - case other: DBFailException => (other.getMessage == message) && (getClass == other.getClass) - case _ => false - } - } -} - -object DBFailException { - def apply(message: String): DBFailException = new DBFailException(message) -} diff --git a/core/src/main/scala/za/co/absa/fadb/status/StatusException.scala b/core/src/main/scala/za/co/absa/fadb/status/StatusException.scala index 86dab624..7105c673 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/StatusException.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/StatusException.scala @@ -16,60 +16,11 @@ package za.co.absa.fadb.status -import za.co.absa.fadb.exceptions.DBFailException - -/** - * Exception caused by status signaling a failure in DB function execution - * @param status - represent the status information returned from the function call - */ -class StatusException(val status:FunctionStatus) extends DBFailException(status.statusText) { - - override def equals(obj: Any): Boolean = { - obj match { - case other: StatusException => (other.status == status) && (getClass == other.getClass) - case _ => false - } - } -} - -object StatusException { - - def apply(status: FunctionStatus): StatusException = new StatusException(status) - def apply(status: Int, statusText: String): StatusException = new StatusException(FunctionStatus(status, statusText)) - - class ServerMisconfigurationException(status:FunctionStatus) extends StatusException(status) - - class DataConflictException(status:FunctionStatus) extends StatusException(status) - - class DataNotFoundException(status:FunctionStatus) extends StatusException(status) - - class ErrorInDataException(status:FunctionStatus) extends StatusException(status) - - class OtherStatusException(status:FunctionStatus) extends StatusException(status) - - object ServerMisconfigurationException { - def apply(status: FunctionStatus): ServerMisconfigurationException = new ServerMisconfigurationException(status) - def apply(status: Int, statusText: String): ServerMisconfigurationException = new ServerMisconfigurationException(FunctionStatus(status, statusText)) - } - - object DataConflictException { - def apply(status: FunctionStatus): DataConflictException = new DataConflictException(status) - def apply(status: Int, statusText: String): DataConflictException = new DataConflictException(FunctionStatus(status, statusText)) - } - - object DataNotFoundException { - def apply(status: FunctionStatus): DataNotFoundException = new DataNotFoundException(status) - def apply(status: Int, statusText: String): DataNotFoundException = new DataNotFoundException(FunctionStatus(status, statusText)) - } - - object ErrorInDataException { - def apply(status: FunctionStatus): ErrorInDataException = new ErrorInDataException(status) - def apply(status: Int, statusText: String): ErrorInDataException = new ErrorInDataException(FunctionStatus(status, statusText)) - } - - object OtherStatusException { - def apply(status: FunctionStatus): OtherStatusException = new OtherStatusException(status) - def apply(status: Int, statusText: String): OtherStatusException = new OtherStatusException(FunctionStatus(status, statusText)) - } - -} +sealed abstract class StatusException(val status: FunctionStatus) extends Exception(status.statusText) + +final case class ServerMisconfigurationException(override val status: FunctionStatus) extends StatusException(status) +final case class DataConflictException(override val status: FunctionStatus) extends StatusException(status) +final case class DataNotFoundException(override val status: FunctionStatus) extends StatusException(status) +final case class ErrorInDataException(override val status: FunctionStatus) extends StatusException(status) +final case class OtherStatusException(override val status: FunctionStatus) extends StatusException(status) +final case class StatusOutOfRangeException(override val status: FunctionStatus) extends StatusException(status) diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala new file mode 100644 index 00000000..e8833f5e --- /dev/null +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala @@ -0,0 +1,23 @@ +package za.co.absa.fadb.status.handling + +import za.co.absa.fadb.FunctionStatusWithData +import za.co.absa.fadb.status._ + +trait QueryStatusHandling { + protected def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] +} + +trait StandardQueryStatusHandling extends QueryStatusHandling { + override protected def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] = { + val functionStatus = statusWithData.functionStatus + functionStatus.statusCode / 10 match { + case 1 => Right(statusWithData.data) + case 2 => Left(ServerMisconfigurationException(functionStatus)) + case 3 => Left(DataConflictException(functionStatus)) + case 4 => Left(DataNotFoundException(functionStatus)) + case 5 | 6 | 7 | 8 => Left(ErrorInDataException(functionStatus)) + case 9 => Left(OtherStatusException(functionStatus)) + case _ => Left(StatusOutOfRangeException(functionStatus)) + } + } +} diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala deleted file mode 100644 index 0742bf20..00000000 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb.status.handling.implementations - -import za.co.absa.fadb.exceptions.DBFailException -import za.co.absa.fadb.status.FunctionStatus -import za.co.absa.fadb.status.StatusException._ -import za.co.absa.fadb.status.handling.StatusHandling - -import scala.util.{Failure, Success, Try} - -/** - * A mix-in trait for [[za.co.absa.fadb.DBFunction DBFunction]] for standard handling of `status` and `statusText` fields. - */ -trait StandardStatusHandling extends StatusHandling { - override protected def checkStatus(status: FunctionStatus): Try[FunctionStatus] = { - status.statusCode / 10 match { - case 1 => Success(status) - case 2 => Failure(ServerMisconfigurationException(status)) - case 3 => Failure(DataConflictException(status)) - case 4 => Failure(DataNotFoundException(status)) - case 5 | 6 | 7 | 8 => Failure(ErrorInDataException(status)) - case 9 => Failure(OtherStatusException(status)) - case _ => Failure(DBFailException(s"Status out of range - with status: ${status.statusCode} and status text: '${status.statusText}'")) - } - } -} diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/UserDefinedStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/UserDefinedStatusHandling.scala deleted file mode 100644 index eb2905ec..00000000 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/UserDefinedStatusHandling.scala +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb.status.handling.implementations - -import za.co.absa.fadb.status.handling.StatusHandling -import za.co.absa.fadb.status.{FunctionStatus, StatusException} - -import scala.util.{Failure, Success, Try} - -/** - * - */ -trait UserDefinedStatusHandling extends StatusHandling { - def OKStatuses: Set[Integer] - - def checkStatus(status: FunctionStatus): Try[FunctionStatus] = { - if (OKStatuses.contains(status.statusCode)) { - Success(status) - } else { - Failure(StatusException(status)) - } - } -} diff --git a/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala b/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala index 51fcb04a..5c6cd2e3 100644 --- a/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala @@ -1,69 +1,69 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb - -import cats.implicits._ -import org.scalatest.funsuite.AnyFunSuite -import za.co.absa.fadb.DBFunction.DBSingleResultFunction -import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention - -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future - -class DBFunctionSuite extends AnyFunSuite { - - private def neverHappens: Nothing = { - throw new Exception("Should never get here") - } - - class EngineThrow extends DBEngine[Future] { - override def run[R](query: QueryType[R]): Future[Seq[R]] = neverHappens - } - - private object FooNamed extends DBSchema - private object FooNameless extends DBSchema("") - - test("Function name check"){ - - class MyFunction(functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, val dbEngine: EngineThrow) - extends DBSingleResultFunction[Unit, Unit, EngineThrow, Future](None) { - - override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens - } - - val fnc1 = new MyFunction()(FooNamed, new EngineThrow) - val fnc2 = new MyFunction()(FooNameless, new EngineThrow) - - assert(fnc1.functionName == "foo_named.my_function") - assert(fnc2.functionName == "my_function") - } - - test("Function name override check"){ - class MyFunction(implicit override val schema: DBSchema, val dbEngine: EngineThrow) - extends DBSingleResultFunction[Unit, Unit, EngineThrow, Future](Some("bar")) { - - override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens - } - - val fnc1 = new MyFunction()(FooNamed, new EngineThrow) - val fnc2 = new MyFunction()(FooNameless, new EngineThrow) - - assert(fnc1.functionName == "foo_named.bar") - assert(fnc2.functionName == "bar") - } - -} +///* +// * Copyright 2022 ABSA Group Limited +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package za.co.absa.fadb +// +//import cats.implicits._ +//import org.scalatest.funsuite.AnyFunSuite +//import za.co.absa.fadb.DBFunction.DBSingleResultFunction +//import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention +// +//import scala.concurrent.ExecutionContext.Implicits.global +//import scala.concurrent.Future +// +//class DBFunctionSuite extends AnyFunSuite { +// +// private def neverHappens: Nothing = { +// throw new Exception("Should never get here") +// } +// +// class EngineThrow extends DBEngine[Future] { +// override def run[R](query: QueryType[R]): Future[Seq[R]] = neverHappens +// } +// +// private object FooNamed extends DBSchema +// private object FooNameless extends DBSchema("") +// +// test("Function name check"){ +// +// class MyFunction(functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, val dbEngine: EngineThrow) +// extends DBSingleResultFunction[Unit, Unit, EngineThrow, Future](None) { +// +// override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens +// } +// +// val fnc1 = new MyFunction()(FooNamed, new EngineThrow) +// val fnc2 = new MyFunction()(FooNameless, new EngineThrow) +// +// assert(fnc1.functionName == "foo_named.my_function") +// assert(fnc2.functionName == "my_function") +// } +// +// test("Function name override check"){ +// class MyFunction(implicit override val schema: DBSchema, val dbEngine: EngineThrow) +// extends DBSingleResultFunction[Unit, Unit, EngineThrow, Future](Some("bar")) { +// +// override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens +// } +// +// val fnc1 = new MyFunction()(FooNamed, new EngineThrow) +// val fnc2 = new MyFunction()(FooNameless, new EngineThrow) +// +// assert(fnc1.functionName == "foo_named.bar") +// assert(fnc2.functionName == "bar") +// } +// +//} diff --git a/core/src/test/scala/za/co/absa/fadb/status/StatusExceptionSuite.scala b/core/src/test/scala/za/co/absa/fadb/status/StatusExceptionSuite.scala index 6211a3a9..48f99bf1 100644 --- a/core/src/test/scala/za/co/absa/fadb/status/StatusExceptionSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/status/StatusExceptionSuite.scala @@ -1,52 +1,52 @@ -/* - * Copyright 2022ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb.status - -import org.scalatest.funsuite.AnyFunSuite -import za.co.absa.fadb.status.StatusException._ - -class StatusExceptionSuite extends AnyFunSuite { - test("Test equals - when they are the same") { - val statusException = DataConflictException(FunctionStatus(10, "OK")) - val otherStatusException = DataConflictException(10, "OK") - - assert(statusException == otherStatusException) - } - - test("Test equals - when they are different") { - val statusException = DataNotFoundException(10, "OK") - val otherStatusException = DataNotFoundException(10, "Hello") - val anotherStatusException = DataNotFoundException(11, "OK") - - assert(statusException != otherStatusException) - assert(statusException != anotherStatusException) - } - - test("Test equals - when values are same but classes differ") { - val statusException = StatusException(10, "OK") - val otherStatusException = ServerMisconfigurationException(10, "OK") - - assert(statusException != otherStatusException) - } - - test("Test equals - when values are same but classes inheritance differ") { - val statusException = StatusException(10, "OK") - val otherException = new ClassNotFoundException() - - assert(statusException != otherException) - } -} +///* +// * Copyright 2022ABSA Group Limited +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package za.co.absa.fadb.status +// +//import org.scalatest.funsuite.AnyFunSuite +//import za.co.absa.fadb.status.StatusException._ +// +//class StatusExceptionSuite extends AnyFunSuite { +// test("Test equals - when they are the same") { +// val statusException = DataConflictException(FunctionStatus(10, "OK")) +// val otherStatusException = DataConflictException(10, "OK") +// +// assert(statusException == otherStatusException) +// } +// +// test("Test equals - when they are different") { +// val statusException = DataNotFoundException(10, "OK") +// val otherStatusException = DataNotFoundException(10, "Hello") +// val anotherStatusException = DataNotFoundException(11, "OK") +// +// assert(statusException != otherStatusException) +// assert(statusException != anotherStatusException) +// } +// +// test("Test equals - when values are same but classes differ") { +// val statusException = StatusException(10, "OK") +// val otherStatusException = ServerMisconfigurationException(10, "OK") +// +// assert(statusException != otherStatusException) +// } +// +// test("Test equals - when values are same but classes inheritance differ") { +// val statusException = StatusException(10, "OK") +// val otherException = new ClassNotFoundException() +// +// assert(statusException != otherException) +// } +//} diff --git a/core/src/test/scala/za/co/absa/fadb/status/StatusHandlingSuite.scala b/core/src/test/scala/za/co/absa/fadb/status/StatusHandlingSuite.scala index 5cb6d80e..f3b76f91 100644 --- a/core/src/test/scala/za/co/absa/fadb/status/StatusHandlingSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/status/StatusHandlingSuite.scala @@ -1,43 +1,43 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb.status - -import org.scalatest.funsuite.AnyFunSuite -import za.co.absa.fadb.DBFunctionFabric -import za.co.absa.fadb.naming.NamingConvention -import za.co.absa.fadb.naming.implementations.SnakeCaseNaming -import za.co.absa.fadb.status.handling.StatusHandling - -class StatusHandlingSuite extends AnyFunSuite { - test("Fields to select filled with default values") { - trait FooDBFunction extends DBFunctionFabric { - override def fieldsToSelect: Seq[String] = Seq("alpha", "beta") - } - - class StatusHandlingForTest extends FooDBFunction with StatusHandling { - override def functionName: String = "Never needed" - override def namingConvention: NamingConvention = SnakeCaseNaming.Implicits.namingConvention - - override protected def checkStatus(status: FunctionStatus) = throw new Exception("Should never get here") - override def fieldsToSelect: Seq[String] = super.fieldsToSelect - } - - val statusHandling = new StatusHandlingForTest - assert(statusHandling.fieldsToSelect == Seq("status", "status_text", "alpha", "beta")) - - } -} +///* +// * Copyright 2022 ABSA Group Limited +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package za.co.absa.fadb.status +// +//import org.scalatest.funsuite.AnyFunSuite +//import za.co.absa.fadb.DBFunctionFabric +//import za.co.absa.fadb.naming.NamingConvention +//import za.co.absa.fadb.naming.implementations.SnakeCaseNaming +//import za.co.absa.fadb.status.handling.StatusHandling +// +//class StatusHandlingSuite extends AnyFunSuite { +// test("Fields to select filled with default values") { +// trait FooDBFunction extends DBFunctionFabric { +// override def fieldsToSelect: Seq[String] = Seq("alpha", "beta") +// } +// +// class StatusHandlingForTest extends FooDBFunction with StatusHandling { +// override def functionName: String = "Never needed" +// override def namingConvention: NamingConvention = SnakeCaseNaming.Implicits.namingConvention +// +// override protected def checkStatus(status: FunctionStatus) = throw new Exception("Should never get here") +// override def fieldsToSelect: Seq[String] = super.fieldsToSelect +// } +// +// val statusHandling = new StatusHandlingForTest +// assert(statusHandling.fieldsToSelect == Seq("status", "status_text", "alpha", "beta")) +// +// } +//} diff --git a/core/src/test/scala/za/co/absa/fadb/status/UserDefinedStatusHandlingSuite.scala b/core/src/test/scala/za/co/absa/fadb/status/UserDefinedStatusHandlingSuite.scala deleted file mode 100644 index 13347c7d..00000000 --- a/core/src/test/scala/za/co/absa/fadb/status/UserDefinedStatusHandlingSuite.scala +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb.status - -import org.scalatest.funsuite.AnyFunSuite -import za.co.absa.fadb.naming.NamingConvention -import za.co.absa.fadb.naming.implementations.SnakeCaseNaming -import za.co.absa.fadb.status.handling.implementations.UserDefinedStatusHandling - -import scala.util.{Failure, Success, Try} - -class UserDefinedStatusHandlingSuite extends AnyFunSuite { - test("Check user defined status") { - class UserDefinedStatusHandlingForTest(val OKStatuses: Set[Integer]) extends UserDefinedStatusHandling { - override def checkStatus(status: FunctionStatus): Try[FunctionStatus] = super.checkStatus(status) - override def functionName: String = "Never needed" - override def namingConvention: NamingConvention = SnakeCaseNaming.Implicits.namingConvention - } - - val statusHandling = new UserDefinedStatusHandlingForTest(Set(200, 201)) - - val oK = FunctionStatus(200, "OK") - val alsoOK = FunctionStatus(201, "Also OK") - val notOK = FunctionStatus(500, "Not OK") - assert(statusHandling.checkStatus(oK) == Success(oK)) - assert(statusHandling.checkStatus(alsoOK) == Success(alsoOK)) - assert(statusHandling.checkStatus(notOK) == Failure(new StatusException(notOK))) - } -} diff --git a/core/src/test/scala/za/co/absa/fadb/status/implementations/StandardStatusHandlingSuite.scala b/core/src/test/scala/za/co/absa/fadb/status/implementations/StandardStatusHandlingSuite.scala deleted file mode 100644 index 6decf32e..00000000 --- a/core/src/test/scala/za/co/absa/fadb/status/implementations/StandardStatusHandlingSuite.scala +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb.status.implementations - -import org.scalatest.funsuite.AnyFunSuite -import za.co.absa.fadb.exceptions.DBFailException -import za.co.absa.fadb.naming.NamingConvention -import za.co.absa.fadb.naming.implementations.SnakeCaseNaming -import za.co.absa.fadb.status.{FunctionStatus, StatusException} -import za.co.absa.fadb.status.StatusException._ -import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling - -import scala.reflect.ClassTag -import scala.util.{Failure, Success, Try} - -class StandardStatusHandlingSuite extends AnyFunSuite { - test("Verify checkStatus error mapping") { - class StandardStatusHandlingForTest extends StandardStatusHandling { - override def checkStatus(status: FunctionStatus): Try[FunctionStatus] = super.checkStatus(status) - override def checkStatus(status: Integer, statusText: String): Try[FunctionStatus] = super.checkStatus(status, statusText) - override def functionName: String = "Never needed" - override def namingConvention: NamingConvention = SnakeCaseNaming.Implicits.namingConvention - } - - def assertCheckStatusFailure[F <: StatusException](status: Int, statusText: String) - (implicit classTag: ClassTag[F], checker: StandardStatusHandlingForTest): Unit = { - - val failure = intercept[F] { - checker.checkStatus(status, statusText).get - } - assert(failure.status == FunctionStatus(status, statusText)) - } - implicit val standardStatusHandling: StandardStatusHandlingForTest = new StandardStatusHandlingForTest - - assert(standardStatusHandling.checkStatus(FunctionStatus(10, "OK")) == Success(FunctionStatus(10, "OK"))) - assertCheckStatusFailure[ServerMisconfigurationException](21, "Server is wrongly set up") - assertCheckStatusFailure[DataConflictException](31, "Referenced data does not allow execution of the request") - assertCheckStatusFailure[DataNotFoundException](42, "Detail record not found") - assertCheckStatusFailure[ErrorInDataException](58, "Some incorrect data") - assertCheckStatusFailure[ErrorInDataException](69, "Missing value for field XYZ") - assertCheckStatusFailure[ErrorInDataException](73, "Value ABC is out of range") - assertCheckStatusFailure[ErrorInDataException](84, "Json value of field FF is missing property PPP") - assertCheckStatusFailure[OtherStatusException](95, "This is a special error") - - val status = 101 - val statusText = "Server is wrongly set up" - val expectedFailure = Failure(DBFailException(s"Status out of range - with status: $status and status text: '${statusText}'")) - assert(standardStatusHandling.checkStatus(101, "Server is wrongly set up") == expectedFailure) - } -} diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStandardStatusHandlingTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStandardStatusHandlingTest.scala new file mode 100644 index 00000000..3ef7654e --- /dev/null +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStandardStatusHandlingTest.scala @@ -0,0 +1,26 @@ +package za.co.absa.fadb.doobie + +import cats.effect.IO +import cats.effect.unsafe.implicits.global +import doobie.implicits.toSqlInterpolator +import doobie.util.Read +import doobie.util.fragment.Fragment +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStandardStatusHandling + +class DoobieFunctionWithStandardStatusHandlingTest extends AnyFunSuite with DoobieTest { + class CreateActor(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) + extends DoobieSingleResultFunctionWithStandardStatusHandling[CreateActorRequestBody, Int, IO] { + + override def sql(values: CreateActorRequestBody)(implicit read: Read[StatusWithData[Int]]): Fragment = + sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" + } + + test("whatever") { + val createActor = new CreateActor()(Runs, new DoobieEngine(transactor)) + val result = createActor(CreateActorRequestBody("Pavel", "Marek")).unsafeRunSync() + println(result) + assert(result.isRight) + } +} diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala index d8c2f7a5..04bf2f45 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala @@ -1,51 +1,50 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb.doobie - -import cats.effect.IO -import cats.effect.unsafe.implicits.global -import doobie.Fragment -import doobie.implicits.toSqlInterpolator -import doobie.util.Read -import org.scalatest.funsuite.AnyFunSuite -import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStatusSupport -import za.co.absa.fadb.status.FunctionStatus -import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling - -class DoobieSingleResultFunctionWithStatusSupportTest extends AnyFunSuite with DoobieTest { - - class CreateActor(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) - extends DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int, IO] - with StandardStatusHandling { - - override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = - sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" - } - - private val createActor = new CreateActor()(Runs, new DoobieEngine(transactor)) - - test("DoobieTest with status handling") { - val requestBody = CreateActorRequestBody("Pavel", "Marek") - createActor.applyWithStatus(requestBody).unsafeRunSync() match { - case Right(success) => - assert(success.functionStatus == FunctionStatus(11, "Actor created")) - case Left(failure) => - fail(failure.failure) - } - } -} +///* +// * Copyright 2022 ABSA Group Limited +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package za.co.absa.fadb.doobie +// +//import cats.effect.IO +//import cats.effect.unsafe.implicits.global +//import doobie.Fragment +//import doobie.implicits.toSqlInterpolator +//import doobie.util.Read +//import org.scalatest.funsuite.AnyFunSuite +//import za.co.absa.fadb.DBSchema +//import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStatusSupport +//import za.co.absa.fadb.status.FunctionStatus +// +//class DoobieSingleResultFunctionWithStatusSupportTest extends AnyFunSuite with DoobieTest { +// +// class CreateActor(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) +// extends DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int, IO] +// with StandardStatusHandling { +// +// override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = +// sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" +// } +// +// private val createActor = new CreateActor()(Runs, new DoobieEngine(transactor)) +// +// test("DoobieTest with status handling") { +// val requestBody = CreateActorRequestBody("Pavel", "Marek") +// createActor.applyWithStatus(requestBody).unsafeRunSync() match { +// case Right(success) => +// assert(success.functionStatus == FunctionStatus(11, "Actor created")) +// case Left(failure) => +// fail(failure.failure) +// } +// } +//} diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala index 0df23dcd..51559f4b 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala @@ -16,10 +16,15 @@ package za.co.absa.fadb.doobie +import cats.Monad import cats.effect.Async +import cats.implicits.toFlatMapOps +import cats.implicits._ import doobie._ import doobie.implicits._ +import doobie.util.Read import za.co.absa.fadb.DBEngine +import za.co.absa.fadb.status.StatusException import scala.language.higherKinds @@ -32,10 +37,12 @@ import scala.language.higherKinds * @param transactor the Doobie transactor for executing SQL queries * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ -class DoobieEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[F] { +class DoobieEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DBEngine[F] { /** The type of Doobie queries that produce `T` */ - type QueryType[T] = DoobieQuery[T] + type QueryType[R] = DoobieQuery[R] +// type QueryWithStatusType[A, B, R] = DoobieQueryWithStatus[R] + type QueryWithStatusType[R] = DoobieQueryWithStatus[R] /** * Executes a Doobie query and returns the result as an `F[Seq[R]]`. @@ -48,6 +55,11 @@ class DoobieEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[ query.fragment.query[R].to[Seq].transact(transactor) } +// private def executeQueryWithStatusHandling[A, B, R](query: DoobieQueryWithStatus[R])(implicit readStatusWithDataR: Read[StatusWithData[R]]): F[Either[StatusException, R]] = { + private def executeQueryWithStatusHandling[R](query: QueryWithStatusType[R])(implicit readStatusWithDataR: Read[StatusWithData[R]]): F[Either[StatusException, R]] = { + query.fragment.query[StatusWithData[R]].unique.transact(transactor).map(query.getResultOrException) + } + /** * Runs a Doobie query and returns the result as an `F[Seq[R]]`. * @@ -56,4 +68,9 @@ class DoobieEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[ */ override def run[R](query: QueryType[R]): F[Seq[R]] = executeQuery(query)(query.readR) + +// override def fetchHeadWithStatusHandling[A, B, R](query: DoobieQueryWithStatus[R]): F[Either[StatusException, R]] = { // refactor in terms of run + override def fetchHeadWithStatusHandling[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] = { // refactor in terms of run + executeQueryWithStatusHandling(query)(query.readStatusWithDataR) + } } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index fdce82ca..6413ce3a 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -22,9 +22,9 @@ import cats.implicits.toFlatMapOps import doobie.util.Read import doobie.util.fragment.Fragment import za.co.absa.fadb.DBFunction._ -import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.{DBFunctionWithStatusHandling, DBSchema} import za.co.absa.fadb.doobie.Results.{FailedResult, ResultWithStatus, SuccessfulResult} -import za.co.absa.fadb.status.FunctionStatus +import za.co.absa.fadb.status.{FunctionStatus, StatusException} import za.co.absa.fadb.status.handling.StatusHandling import scala.language.higherKinds @@ -66,33 +66,42 @@ private[doobie] trait DoobieFunction[I, R] { * @tparam I the input type of the function * @tparam R the result type of the function */ -private[doobie] trait DoobieFunctionWithStatusSupport[I, R] extends StatusHandling { - - /** - * The `Read[R]` instance used to read the query result into `R`. - */ +//private[doobie] trait DoobieFunctionWithStatusSupport[I, R] extends StatusHandling { +// +// /** +// * The `Read[R]` instance used to read the query result into `R`. +// */ +// implicit val readR: Read[R] +// +// /** +// * The `Read[(Int, String, R)]` instance used to read the query result with status into `(Int, String, R)`. +// */ +// implicit val readSelectWithStatus: Read[(Int, String, R)] +// +// /** +// * Generates a Doobie `Fragment` representing the SQL query for the function. +// * +// * @param values the input values for the function +// * @return the Doobie `Fragment` representing the SQL query +// */ +// def sql(values: I)(implicit read: Read[R]): Fragment +// +// /** +// * Generates a `DoobieQuery[(Int, String, R)]` representing the SQL query for the function with status. +// * +// * @param values the input values for the function +// * @return the `DoobieQuery[(Int, String, R)]` representing the SQL query with status +// */ +// protected def query(values: I): DoobieQuery[(Int, String, R)] = new DoobieQuery[(Int, String, R)](sql(values)) +//} + +private[doobie] trait DoobieFunctionWithStandardStatusHandling[I, R] { implicit val readR: Read[R] + implicit def readStatusWithDataR[R](implicit readR: Read[R]): Read[StatusWithData[R]] = Read[(Int, String, R)].map { + case (status, status_text, data) => StatusWithData(status, status_text, data) + } + def sql(values: I)(implicit read: Read[StatusWithData[R]]): Fragment - /** - * The `Read[(Int, String, R)]` instance used to read the query result with status into `(Int, String, R)`. - */ - implicit val readSelectWithStatus: Read[(Int, String, R)] - - /** - * Generates a Doobie `Fragment` representing the SQL query for the function. - * - * @param values the input values for the function - * @return the Doobie `Fragment` representing the SQL query - */ - def sql(values: I)(implicit read: Read[R]): Fragment - - /** - * Generates a `DoobieQuery[(Int, String, R)]` representing the SQL query for the function with status. - * - * @param values the input values for the function - * @return the `DoobieQuery[(Int, String, R)]` representing the SQL query with status - */ - protected def query(values: I): DoobieQuery[(Int, String, R)] = new DoobieQuery[(Int, String, R)](sql(values)) } /** @@ -101,52 +110,68 @@ private[doobie] trait DoobieFunctionWithStatusSupport[I, R] extends StatusHandli */ object DoobieFunction { + abstract class DoobieSingleResultFunctionWithStandardStatusHandling[I, R, F[_]: Async: Monad]( + functionNameOverride: Option[String] = None + )(implicit + override val schema: DBSchema, + val dbEngine: DoobieEngine[F], + val readR: Read[R], + val readSelectWithStatus: Read[StatusWithData[R]] + ) extends DBSingleResultWithStatusHandlingFunction[I, R, DoobieEngine[F], F](functionNameOverride) + with DoobieFunctionWithStandardStatusHandling[I, R] { + + protected def query(values: I): dbEngine.QueryWithStatusType[R] = new DoobieQueryWithStatus[R](sql(values)) + } + /** - * `DoobieSingleResultFunction` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type. - * It represents a database function that returns a single result. - * - * @param functionNameOverride the optional override for the function name - * @param schema the database schema - * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries - * @param readR the `Read[R]` instance used to read the query result into `R` - * @tparam F the effect type, which must have an `Async` and a `Monad` instance + * `DoobieSingleResultFunction` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type. + * It represents a database function that returns a single result. + * + * @param functionNameOverride the optional override for the function name + * @param schema the database schema + * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param readR the `Read[R]` instance used to read the query result into `R` + * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieSingleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, + implicit + override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] ) extends DBSingleResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) - with DoobieFunction[I, R] + with DoobieFunction[I, R] /** - * `DoobieMultipleResultFunction` is an abstract class that extends `DBMultipleResultFunction` with `DoobiePgEngine` as the engine type. - * It represents a database function that returns multiple results. - * - * @param functionNameOverride the optional override for the function name - * @param schema the database schema - * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries - * @param readR the `Read[R]` instance used to read the query result into `R` - * @tparam F the effect type, which must have an `Async` and a `Monad` instance + * `DoobieMultipleResultFunction` is an abstract class that extends `DBMultipleResultFunction` with `DoobiePgEngine` as the engine type. + * It represents a database function that returns multiple results. + * + * @param functionNameOverride the optional override for the function name + * @param schema the database schema + * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param readR the `Read[R]` instance used to read the query result into `R` + * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieMultipleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, + implicit + override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] ) extends DBMultipleResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) with DoobieFunction[I, R] /** - * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` with `DoobiePgEngine` as the engine type. - * It represents a database function that returns an optional result. - * - * @param functionNameOverride the optional override for the function name - * @param schema the database schema - * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries - * @param readR the `Read[R]` instance used to read the query result into `R` - * @tparam F the effect type, which must have an `Async` and a `Monad` instance + * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` with `DoobiePgEngine` as the engine type. + * It represents a database function that returns an optional result. + * + * @param functionNameOverride the optional override for the function name + * @param schema the database schema + * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param readR the `Read[R]` instance used to read the query result into `R` + * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieOptionalResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, + implicit + override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] ) extends DBOptionalResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) @@ -154,39 +179,40 @@ object DoobieFunction { /** * `DoobieSingleResultFunctionWithStatusSupport` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type. - * It represents a database function that returns a single result with status. + * It represents a database function that returns a single result with status. * - * @param functionNameOverride the optional override for the function name - * @param schema the database schema - * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries - * @param readR the `Read[R]` instance used to read the query result into `R` - * @param readSelectWithStatus the `Read[(Int, String, R)]` instance used to read the query result with status into `(Int, String, R)` - * @tparam F the effect type, which must have an `Async` and a `Monad` instance + * @param functionNameOverride the optional override for the function name + * @param schema the database schema + * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param readR the `Read[R]` instance used to read the query result into `R` + * @param readSelectWithStatus the `Read[(Int, String, R)]` instance used to read the query result with status into `(Int, String, R)` + * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ - abstract class DoobieSingleResultFunctionWithStatusSupport[I, R, F[_]: Async: Monad]( - functionNameOverride: Option[String] = None - )(implicit override val schema: DBSchema, - val dbEngine: DoobieEngine[F], - val readR: Read[R], - val readSelectWithStatus: Read[(Int, String, R)] - ) extends DBSingleResultFunction[I, (Int, String, R), DoobieEngine[F], F](functionNameOverride) - with DoobieFunctionWithStatusSupport[I, R] { - - /** - * Executes the function with the given input values and returns the result with status. - * - * @param values the input values for the function - * @param monad the `Monad` instance used to chain operations together - * @return the result with status - */ - def applyWithStatus(values: I)(implicit monad: Monad[F]): F[ResultWithStatus[R]] = { - super.apply(values).flatMap { case (status, statusText, result) => - checkStatus(status, statusText) match { - case Success(_) => monad.pure(Right(SuccessfulResult[R](FunctionStatus(status, statusText), result))) - case Failure(e) => monad.pure(Left(FailedResult(FunctionStatus(status, statusText), e))) - } - } - } - } +// abstract class DoobieSingleResultFunctionWithStatusSupport[I, R, F[_]: Async: Monad]( +// functionNameOverride: Option[String] = None +// )(implicit +// override val schema: DBSchema, +// val dbEngine: DoobieEngine[F], +// val readR: Read[R], +// val readSelectWithStatus: Read[(Int, String, R)] +// ) extends DBSingleResultFunction[I, (Int, String, R), DoobieEngine[F], F](functionNameOverride) +// with DoobieFunctionWithStatusSupport[I, R] { +// +// /** +// * Executes the function with the given input values and returns the result with status. +// * +// * @param values the input values for the function +// * @param monad the `Monad` instance used to chain operations together +// * @return the result with status +// */ +// def applyWithStatus(values: I)(implicit monad: Monad[F]): F[ResultWithStatus[R]] = { +// super.apply(values).flatMap { case (status, statusText, result) => +// checkStatus(status, statusText) match { +// case Success(_) => monad.pure(Right(SuccessfulResult[R](FunctionStatus(status, statusText), result))) +// case Failure(e) => monad.pure(Left(FailedResult(FunctionStatus(status, statusText), e))) +// } +// } +// } +// } } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala index b9262148..302d33ec 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala @@ -18,7 +18,11 @@ package za.co.absa.fadb.doobie import doobie.util.Read import doobie.util.fragment.Fragment -import za.co.absa.fadb.Query +import za.co.absa.fadb.status.handling.StandardQueryStatusHandling +import za.co.absa.fadb.status.{FunctionStatus, StatusException} +import za.co.absa.fadb.{FunctionStatusWithData, Query, QueryWithStatus} + +import scala.language.higherKinds /** * `DoobieQuery` is a class that extends `Query` with `R` as the result type. @@ -28,3 +32,19 @@ import za.co.absa.fadb.Query * @param readR the `Read[R]` instance used to read the query result into `R` */ class DoobieQuery[R: Read](val fragment: Fragment)(implicit val readR: Read[R]) extends Query[R] + +// QueryStatusHandling has to be mixed-in for the checkStatus method implementation +class DoobieQueryWithStatus[R](val fragment: Fragment)(implicit val readStatusWithDataR: Read[StatusWithData[R]]) + extends QueryWithStatus[StatusWithData[R], R, R] with StandardQueryStatusHandling { +// extends QueryWithStatus[A, B, R] { + + override def processStatus(initialResult: StatusWithData[R]): FunctionStatusWithData[R] = + FunctionStatusWithData(FunctionStatus(initialResult.status, initialResult.status_text), initialResult.data) + + override def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[R]): Either[StatusException, R] = + checkStatus(statusWithData) +} + +//class DoobieQueryWithStandardStatusHandling[R: Read](override val fragment: Fragment)(implicit override val readStatusWithDataR: Read[StatusWithData[R]]) +//// extends DoobieQueryWithStatus[A, B, R](fragment)(readStatusWithDataR) with StandardQueryStatusHandling +// extends DoobieQueryWithStatus[R](fragment)(readStatusWithDataR) with StandardQueryStatusHandling diff --git a/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala b/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala index 42eac7d4..6765d6d7 100644 --- a/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala +++ b/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala @@ -27,7 +27,6 @@ import scala.concurrent.Future import DatasetSchema._ import cats.implicits._ import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBSingleResultFunction} -import za.co.absa.fadb.status.handling.implementations.UserDefinedStatusHandling import scala.concurrent.ExecutionContext.Implicits.global diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala index fce7a6d4..9a54a618 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala @@ -21,29 +21,31 @@ import za.co.absa.fadb.DBEngine import scala.concurrent.{ExecutionContext, Future} import slick.jdbc.PostgresProfile.api._ - import cats.implicits._ +import za.co.absa.fadb.status.StatusException import scala.language.higherKinds +import slick.jdbc.{GetResult, PositionedResult} /** - * [[DBEngine]] based on the Slick library in the Postgres flavor - * @param db - the Slick database - */ + * [[DBEngine]] based on the Slick library in the Postgres flavor + * @param db - the Slick database + */ class SlickPgEngine(val db: Database)(implicit val executor: ExecutionContext) extends DBEngine[Future] { /** - * The type of Queries for Slick - * @tparam T - the return type of the query - */ - type QueryType[T] = SlickQuery[T] + * The type of Queries for Slick + * @tparam T - the return type of the query + */ + type QueryType[R] = SlickQuery[R] + type QueryWithStatusType[R] = SlickQueryWithStatus[R] /** - * Execution using Slick - * @param query - the Slick query to execute - * @tparam R - return the of the query - * @return - sequence of the results of database query - */ + * Execution using Slick + * @param query - the Slick query to execute + * @tparam R - return the of the query + * @return - sequence of the results of database query + */ override protected def run[R](query: QueryType[R]): Future[Seq[R]] = { // It can be expected that a GetResult will be passed into the run function as converter. // Unfortunately it has to be recreated to be used by Slick @@ -51,4 +53,11 @@ class SlickPgEngine(val db: Database)(implicit val executor: ExecutionContext) e db.run(slickAction) } +// override def fetchHeadWithStatusHandling[_, _, R](query: SlickQueryWithStatus[R]): Future[Either[StatusException, R]] = { + override def fetchHeadWithStatusHandling[R](query: QueryWithStatusType[R]): Future[Either[StatusException, R]] = { + implicit val getPositionedResult: GetResult[PositionedResult] = GetResult(r => r) + db.run(query.sql.as[PositionedResult]).map { results => + query.getResultOrException(results.head) + } + } } diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala index 8cc6335a..c6aa1548 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala @@ -16,14 +16,33 @@ package za.co.absa.fadb.slick -import slick.jdbc.{GetResult, SQLActionBuilder} -import za.co.absa.fadb.Query +import slick.jdbc.{GetResult, PositionedResult, SQLActionBuilder} +import za.co.absa.fadb.status.{FunctionStatus, StatusException} +import za.co.absa.fadb.{FunctionStatusWithData, Query, QueryWithStatus} /** - * SQL query representation for Slick - * @param sql - the SQL query in Slick format - * @param getResult - the converting function, that converts the [[slick.jdbc.PositionedResult slick.PositionedResult]] (the result of Slick - * execution) into the desire `R` type - * @tparam R - the return type of the query - */ + * SQL query representation for Slick + * @param sql - the SQL query in Slick format + * @param getResult - the converting function, that converts the [[slick.jdbc.PositionedResult slick.PositionedResult]] (the result of Slick + * execution) into the desire `R` type + * @tparam R - the return type of the query + */ class SlickQuery[R](val sql: SQLActionBuilder, val getResult: GetResult[R]) extends Query[R] + +// QueryStatusHandling has to be mixed-in for the checkStatus method implementation +abstract class SlickQueryWithStatus[R](val sql: SQLActionBuilder, val getResult: GetResult[R]) + extends QueryWithStatus[PositionedResult, PositionedResult, R] { + + override def processStatus(initialResult: PositionedResult): FunctionStatusWithData[PositionedResult] = { + val status: Int = initialResult.<< + val statusText: String = initialResult.<< + FunctionStatusWithData(FunctionStatus(status, statusText), initialResult) + } + + override def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[PositionedResult]): Either[StatusException, R] = { + checkStatus(statusWithData) match { + case Left(statusException) => Left(statusException) + case Right(value) => Right(getResult(value)) + } + } +} From a3784d77958102e30f3578019c59f3362c09622e Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 7 Dec 2023 21:01:31 +0100 Subject: [PATCH 22/90] tmp commit --- .../main/scala/za/co/absa/fadb/DBEngine.scala | 10 +- .../scala/za/co/absa/fadb/DBFunction.scala | 28 +-- .../za/co/absa/fadb/DBFunctionFabric.scala | 4 +- .../main/scala/za/co/absa/fadb/Query.scala | 9 +- .../fadb/status/handling/StatusHandling.scala | 2 +- ...ala => DoobieFunctionWithStatusTest.scala} | 6 +- ...eResultFunctionWithStatusSupportTest.scala | 50 ------ ...ieSingleResultFunctionWithStatusTest.scala | 45 +++++ .../za/co/absa/fadb/doobie/DoobieEngine.scala | 7 +- .../co/absa/fadb/doobie/DoobieFunction.scala | 162 ++++++------------ .../za/co/absa/fadb/doobie/DoobieQuery.scala | 4 - .../fadb/slick/FaDbPostgresProfileSuite.scala | 14 +- .../SlickMultipleResultFunctionTest.scala | 5 +- .../SlickOptionalResultFunctionTest.scala | 10 +- ...ckSingleResultFunctionWithStatusTest.scala | 35 ++++ .../za/co/absa/fadb/slick/SlickTest.scala | 1 + .../za/co/absa/fadb/slick/SlickFunction.scala | 103 +++++++---- .../SlickFunctionWithStatusSupport.scala | 132 +++++++------- .../za/co/absa/fadb/slick/SlickPgEngine.scala | 2 +- .../za/co/absa/fadb/slick/SlickQuery.scala | 5 +- 20 files changed, 308 insertions(+), 326 deletions(-) rename doobie/src/it/scala/za/co/absa/fadb/doobie/{DoobieFunctionWithStandardStatusHandlingTest.scala => DoobieFunctionWithStatusTest.scala} (80%) delete mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala create mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala create mode 100644 slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala diff --git a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala index 882b5fe2..90ba3ea6 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala @@ -32,7 +32,6 @@ abstract class DBEngine[F[_]: Monad] { * @tparam T - the return type of the query */ type QueryType[T] <: Query[T] -// type QueryWithStatusType[A, B, R] <: QueryWithStatus[A, B, R] type QueryWithStatusType[R] <: QueryWithStatus[_, _, R] /** @@ -43,8 +42,13 @@ abstract class DBEngine[F[_]: Monad] { */ protected def run[R](query: QueryType[R]): F[Seq[R]] -// def fetchHeadWithStatusHandling[A, B, R](query: QueryWithStatusType[A, B, R]): F[Either[StatusException, R]] - def fetchHeadWithStatusHandling[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] + /** + * The actual query executioner of the queries of the engine + * @param query - the query to execute + * @tparam R - return the of the query + * @return - sequence of the results of database query + */ + def fetchHeadWithStatus[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] /** * Public method to execute when query is expected to return multiple results diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 299b5d64..67b2759f 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -17,7 +17,6 @@ package za.co.absa.fadb import cats.Monad -import za.co.absa.fadb.naming.NamingConvention import za.co.absa.fadb.status.StatusException import scala.language.higherKinds @@ -57,8 +56,8 @@ abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverr protected def optionalResult(values: I): F[Option[R]] = dBEngine.fetchHeadOption(query(values)) } -abstract class DBFunctionWithStatusHandling[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) - (implicit override val schema: DBSchema, val dBEngine: E) +abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) + (implicit override val schema: DBSchema, val dBEngine: E) extends DBFunctionFabric(functionNameOverride) { // A constructor that takes only the mandatory parameters and uses default values for the optional ones @@ -75,9 +74,7 @@ abstract class DBFunctionWithStatusHandling[I, R, E <: DBEngine[F], F[_]: Monad] */ protected def query(values: I): dBEngine.QueryWithStatusType[R] - protected def singleResultWithStatusHandling(values: I): F[Either[StatusException, R]] = { - dBEngine.fetchHeadWithStatusHandling(query(values)) - } + def apply(values: I): F[Either[StatusException, R]] = dBEngine.fetchHeadWithStatus(query(values)) } object DBFunction { @@ -168,23 +165,4 @@ object DBFunction { */ def apply(values: I): F[Option[R]] = optionalResult(values) } - -// abstract class DBSingleResultWithStatusHandlingFunction[I, R, E <: DBEngine[F], F[_] : Monad](functionNameOverride: Option[String] = None) -// (implicit schema: DBSchema, dBEngine: E) -// extends DBFunctionWithStatusHandling[I, R, E, F](functionNameOverride) { -// -// // A constructor that takes only the mandatory parameters and uses default values for the optional ones -// def this()(implicit schema: DBSchema, dBEngine: E) = this(None) -// -// // A constructor that allows specifying the function name as a string, but not as an option -// def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) -// -// /** -// * For easy and convenient execution of the DB function call -// * -// * @param values - the values to pass over to the database function -// * @return - the value returned from the DB function transformed to scala type `R` -// */ -// def apply(values: I): F[Either[StatusException, R]] = singleResultWithStatusHandling(values) -// } } diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala b/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala index 120577dd..23d0503c 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala @@ -28,7 +28,7 @@ abstract class DBFunctionFabric(functionNameOverride: Option[String])(implicit v * List of fields to select from the DB function. * @return - list of fields to select */ - protected def fieldsToSelect: Seq[String] = Seq.empty + def fieldsToSelect: Seq[String] = Seq.empty /** * Name of the function, based on the class name, unless it is overridden in the constructor @@ -42,5 +42,5 @@ abstract class DBFunctionFabric(functionNameOverride: Option[String])(implicit v } } - def namingConvention: NamingConvention = schema.namingConvention +// def namingConvention: NamingConvention = schema.namingConvention } diff --git a/core/src/main/scala/za/co/absa/fadb/Query.scala b/core/src/main/scala/za/co/absa/fadb/Query.scala index 54243359..b9069612 100644 --- a/core/src/main/scala/za/co/absa/fadb/Query.scala +++ b/core/src/main/scala/za/co/absa/fadb/Query.scala @@ -16,8 +16,8 @@ package za.co.absa.fadb +import za.co.absa.fadb.status.StatusException import za.co.absa.fadb.status.handling.QueryStatusHandling -import za.co.absa.fadb.status.{FunctionStatus, StatusException} /** * The basis for all query types of [[DBEngine]] implementations @@ -26,10 +26,9 @@ import za.co.absa.fadb.status.{FunctionStatus, StatusException} */ trait Query[R] -trait QueryWithStatus[A, B, R] extends QueryStatusHandling { // A initial result, B after status extraction, R final result -//trait QueryWithStatus[R] extends QueryStatusHandling { // A initial result, B after status extraction, R final result -// def processStatus(initialResult: A): FunctionStatusWithData[B] + +trait QueryWithStatus[A, B, R] extends QueryStatusHandling { def processStatus(initialResult: A): FunctionStatusWithData[B] - def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[B]): Either[StatusException, R] // think of typ alias for Either[StatusException, R] + def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[B]): Either[StatusException, R] def getResultOrException(initialResult: A): Either[StatusException, R] = toStatusExceptionOrData(processStatus(initialResult)) } diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala index c0195887..54a6dba5 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala @@ -48,7 +48,7 @@ trait StatusHandling extends DBFunctionFabric { * A mix-in to add the status fields into the SELECT statement * @return a sequence of fields to use in SELECT */ - override protected def fieldsToSelect: Seq[String] = { + override def fieldsToSelect: Seq[String] = { Seq( namingConvention.stringPerConvention(statusField), namingConvention.stringPerConvention(statusTextField) diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStandardStatusHandlingTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusTest.scala similarity index 80% rename from doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStandardStatusHandlingTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusTest.scala index 3ef7654e..7c95df95 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStandardStatusHandlingTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusTest.scala @@ -7,11 +7,11 @@ import doobie.util.Read import doobie.util.fragment.Fragment import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStandardStatusHandling +import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStatus -class DoobieFunctionWithStandardStatusHandlingTest extends AnyFunSuite with DoobieTest { +class DoobieFunctionWithStatusTest extends AnyFunSuite with DoobieTest { class CreateActor(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) - extends DoobieSingleResultFunctionWithStandardStatusHandling[CreateActorRequestBody, Int, IO] { + extends DoobieSingleResultFunctionWithStatus[CreateActorRequestBody, Int, IO] { override def sql(values: CreateActorRequestBody)(implicit read: Read[StatusWithData[Int]]): Fragment = sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala deleted file mode 100644 index 04bf2f45..00000000 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusSupportTest.scala +++ /dev/null @@ -1,50 +0,0 @@ -///* -// * Copyright 2022 ABSA Group Limited -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -// -//package za.co.absa.fadb.doobie -// -//import cats.effect.IO -//import cats.effect.unsafe.implicits.global -//import doobie.Fragment -//import doobie.implicits.toSqlInterpolator -//import doobie.util.Read -//import org.scalatest.funsuite.AnyFunSuite -//import za.co.absa.fadb.DBSchema -//import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStatusSupport -//import za.co.absa.fadb.status.FunctionStatus -// -//class DoobieSingleResultFunctionWithStatusSupportTest extends AnyFunSuite with DoobieTest { -// -// class CreateActor(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) -// extends DoobieSingleResultFunctionWithStatusSupport[CreateActorRequestBody, Int, IO] -// with StandardStatusHandling { -// -// override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = -// sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" -// } -// -// private val createActor = new CreateActor()(Runs, new DoobieEngine(transactor)) -// -// test("DoobieTest with status handling") { -// val requestBody = CreateActorRequestBody("Pavel", "Marek") -// createActor.applyWithStatus(requestBody).unsafeRunSync() match { -// case Right(success) => -// assert(success.functionStatus == FunctionStatus(11, "Actor created")) -// case Left(failure) => -// fail(failure.failure) -// } -// } -//} diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala new file mode 100644 index 00000000..1fdb3c42 --- /dev/null +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala @@ -0,0 +1,45 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.fadb.doobie + +import cats.effect.IO +import cats.effect.unsafe.implicits.global +import doobie.Fragment +import doobie.implicits.toSqlInterpolator +import doobie.util.Read +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStatus + +class DoobieSingleResultFunctionWithStatusTest extends AnyFunSuite with DoobieTest { + + class CreateActor(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) + extends DoobieSingleResultFunctionWithStatus[CreateActorRequestBody, Int, IO] { + + override def sql(values: CreateActorRequestBody)(implicit read: Read[StatusWithData[Int]]): Fragment = { + sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" + } + } + + private val createActor = new CreateActor()(Runs, new DoobieEngine(transactor)) + + test("DoobieTest with status handling") { + val requestBody = CreateActorRequestBody("Pavel", "Marek") + val result = createActor(requestBody).unsafeRunSync() + assert(result.isRight) + } +} diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala index 51559f4b..778be934 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala @@ -29,7 +29,7 @@ import za.co.absa.fadb.status.StatusException import scala.language.higherKinds /** - * `DoobiePgEngine` is a class that extends `DBEngine` with `F` as the effect type. + * `DoobieEngine` is a class that extends `DBEngine` with `F` as the effect type. * It uses Doobie's `Transactor[F]` to execute SQL queries. * * `Async` is needed because Doobie requires it for non-blocking database operations. @@ -41,7 +41,6 @@ class DoobieEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DB /** The type of Doobie queries that produce `T` */ type QueryType[R] = DoobieQuery[R] -// type QueryWithStatusType[A, B, R] = DoobieQueryWithStatus[R] type QueryWithStatusType[R] = DoobieQueryWithStatus[R] /** @@ -55,7 +54,6 @@ class DoobieEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DB query.fragment.query[R].to[Seq].transact(transactor) } -// private def executeQueryWithStatusHandling[A, B, R](query: DoobieQueryWithStatus[R])(implicit readStatusWithDataR: Read[StatusWithData[R]]): F[Either[StatusException, R]] = { private def executeQueryWithStatusHandling[R](query: QueryWithStatusType[R])(implicit readStatusWithDataR: Read[StatusWithData[R]]): F[Either[StatusException, R]] = { query.fragment.query[StatusWithData[R]].unique.transact(transactor).map(query.getResultOrException) } @@ -69,8 +67,7 @@ class DoobieEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DB override def run[R](query: QueryType[R]): F[Seq[R]] = executeQuery(query)(query.readR) -// override def fetchHeadWithStatusHandling[A, B, R](query: DoobieQueryWithStatus[R]): F[Either[StatusException, R]] = { // refactor in terms of run - override def fetchHeadWithStatusHandling[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] = { // refactor in terms of run + override def fetchHeadWithStatus[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] = { executeQueryWithStatusHandling(query)(query.readStatusWithDataR) } } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index 6413ce3a..e658c25c 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -18,17 +18,20 @@ package za.co.absa.fadb.doobie import cats.Monad import cats.effect.kernel.Async -import cats.implicits.toFlatMapOps import doobie.util.Read import doobie.util.fragment.Fragment import za.co.absa.fadb.DBFunction._ -import za.co.absa.fadb.{DBFunctionWithStatusHandling, DBSchema} -import za.co.absa.fadb.doobie.Results.{FailedResult, ResultWithStatus, SuccessfulResult} -import za.co.absa.fadb.status.{FunctionStatus, StatusException} -import za.co.absa.fadb.status.handling.StatusHandling +import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema} import scala.language.higherKinds -import scala.util.{Failure, Success} + +trait DoobieFunctionBase[R] { + + /** + * The `Read[R]` instance used to read the query result into `R`. + */ + implicit val readR: Read[R] +} /** * `DoobieFunction` provides support for executing database functions using Doobie. @@ -36,13 +39,7 @@ import scala.util.{Failure, Success} * @tparam I the input type of the function * @tparam R the result type of the function */ -private[doobie] trait DoobieFunction[I, R] { - - /** - * The `Read[R]` instance used to read the query result into `R`. - */ - implicit val readR: Read[R] - +private[doobie] trait DoobieFunction[I, R] extends DoobieFunctionBase[R] { /** * Generates a Doobie `Fragment` representing the SQL query for the function. * @@ -60,68 +57,59 @@ private[doobie] trait DoobieFunction[I, R] { protected def query(values: I): DoobieQuery[R] = new DoobieQuery[R](sql(values)) } -/** - * `DoobieFunctionWithStatusSupport` provides support for executing database functions with status handling using Doobie. - * - * @tparam I the input type of the function - * @tparam R the result type of the function - */ -//private[doobie] trait DoobieFunctionWithStatusSupport[I, R] extends StatusHandling { -// -// /** -// * The `Read[R]` instance used to read the query result into `R`. -// */ -// implicit val readR: Read[R] -// -// /** -// * The `Read[(Int, String, R)]` instance used to read the query result with status into `(Int, String, R)`. -// */ -// implicit val readSelectWithStatus: Read[(Int, String, R)] -// -// /** -// * Generates a Doobie `Fragment` representing the SQL query for the function. -// * -// * @param values the input values for the function -// * @return the Doobie `Fragment` representing the SQL query -// */ -// def sql(values: I)(implicit read: Read[R]): Fragment -// -// /** -// * Generates a `DoobieQuery[(Int, String, R)]` representing the SQL query for the function with status. -// * -// * @param values the input values for the function -// * @return the `DoobieQuery[(Int, String, R)]` representing the SQL query with status -// */ -// protected def query(values: I): DoobieQuery[(Int, String, R)] = new DoobieQuery[(Int, String, R)](sql(values)) -//} - -private[doobie] trait DoobieFunctionWithStandardStatusHandling[I, R] { - implicit val readR: Read[R] - implicit def readStatusWithDataR[R](implicit readR: Read[R]): Read[StatusWithData[R]] = Read[(Int, String, R)].map { + +private[doobie] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBase[R] { + + /** + * The `Read[StatusWithData[R]]` instance used to read the query result with status into `StatusWithData[R]`. + */ + implicit def readStatusWithDataR(implicit readR: Read[R]): Read[StatusWithData[R]] = Read[(Int, String, R)].map { case (status, status_text, data) => StatusWithData(status, status_text, data) } + + /** + * Generates a Doobie `Fragment` representing the SQL query for the function. + * + * @param values the input values for the function + * @return the Doobie `Fragment` representing the SQL query + */ def sql(values: I)(implicit read: Read[StatusWithData[R]]): Fragment + /** + * Generates a `DoobieQueryWithStatus[R]` representing the SQL query for the function. + * + * @param values the input values for the function + * @return the `DoobieQueryWithStatus[R]` representing the SQL query + */ + protected def query(values: I): DoobieQueryWithStatus[R] = new DoobieQueryWithStatus[R](sql(values)) } /** * `DoobieFunction` is an object that contains several abstract classes extending different types of database functions. - * These classes use Doobie's `Fragment` to represent SQL queries and `DoobiePgEngine` to execute them. + * These classes use Doobie's `Fragment` to represent SQL queries and `DoobieEngine` to execute them. */ object DoobieFunction { - abstract class DoobieSingleResultFunctionWithStandardStatusHandling[I, R, F[_]: Async: Monad]( + /** + * `DoobieSingleResultFunctionWithStatus` is an abstract class that extends `DBSingleResultFunctionWithStatus` with `DoobiePgEngine` as the engine type. + * It represents a database function that returns a single result with status. + * + * @param functionNameOverride the optional override for the function name + * @param schema the database schema + * @param dbEngine the `DoobieEngine` instance used to execute SQL queries + * @param readR the `Read[R]` instance used to read the query result into `R` + * @param readSelectWithStatus the `Read[StatusWithData[R]]` instance used to read the query result with status into `StatusWithData[R]` + * @tparam F the effect type, which must have an `Async` and a `Monad` instance + */ + abstract class DoobieSingleResultFunctionWithStatus[I, R, F[_]: Async: Monad]( functionNameOverride: Option[String] = None - )(implicit - override val schema: DBSchema, + )( + implicit override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R], val readSelectWithStatus: Read[StatusWithData[R]] - ) extends DBSingleResultWithStatusHandlingFunction[I, R, DoobieEngine[F], F](functionNameOverride) - with DoobieFunctionWithStandardStatusHandling[I, R] { - - protected def query(values: I): dbEngine.QueryWithStatusType[R] = new DoobieQueryWithStatus[R](sql(values)) - } + ) extends DBFunctionWithStatus[I, R, DoobieEngine[F], F](functionNameOverride) + with DoobieFunctionWithStatus[I, R] /** * `DoobieSingleResultFunction` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type. @@ -129,13 +117,12 @@ object DoobieFunction { * * @param functionNameOverride the optional override for the function name * @param schema the database schema - * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param dbEngine the `DoobieEngine` instance used to execute SQL queries * @param readR the `Read[R]` instance used to read the query result into `R` * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieSingleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit - override val schema: DBSchema, + implicit override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] ) extends DBSingleResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) @@ -147,13 +134,12 @@ object DoobieFunction { * * @param functionNameOverride the optional override for the function name * @param schema the database schema - * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param dbEngine the `DoobieEngine` instance used to execute SQL queries * @param readR the `Read[R]` instance used to read the query result into `R` * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieMultipleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit - override val schema: DBSchema, + implicit override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] ) extends DBMultipleResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) @@ -165,54 +151,14 @@ object DoobieFunction { * * @param functionNameOverride the optional override for the function name * @param schema the database schema - * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries + * @param dbEngine the `DoobieEngine` instance used to execute SQL queries * @param readR the `Read[R]` instance used to read the query result into `R` * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieOptionalResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit - override val schema: DBSchema, + implicit override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] ) extends DBOptionalResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) with DoobieFunction[I, R] - - /** - * `DoobieSingleResultFunctionWithStatusSupport` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type. - * It represents a database function that returns a single result with status. - * - * @param functionNameOverride the optional override for the function name - * @param schema the database schema - * @param dbEngine the `DoobiePgEngine` instance used to execute SQL queries - * @param readR the `Read[R]` instance used to read the query result into `R` - * @param readSelectWithStatus the `Read[(Int, String, R)]` instance used to read the query result with status into `(Int, String, R)` - * @tparam F the effect type, which must have an `Async` and a `Monad` instance - */ -// abstract class DoobieSingleResultFunctionWithStatusSupport[I, R, F[_]: Async: Monad]( -// functionNameOverride: Option[String] = None -// )(implicit -// override val schema: DBSchema, -// val dbEngine: DoobieEngine[F], -// val readR: Read[R], -// val readSelectWithStatus: Read[(Int, String, R)] -// ) extends DBSingleResultFunction[I, (Int, String, R), DoobieEngine[F], F](functionNameOverride) -// with DoobieFunctionWithStatusSupport[I, R] { -// -// /** -// * Executes the function with the given input values and returns the result with status. -// * -// * @param values the input values for the function -// * @param monad the `Monad` instance used to chain operations together -// * @return the result with status -// */ -// def applyWithStatus(values: I)(implicit monad: Monad[F]): F[ResultWithStatus[R]] = { -// super.apply(values).flatMap { case (status, statusText, result) => -// checkStatus(status, statusText) match { -// case Success(_) => monad.pure(Right(SuccessfulResult[R](FunctionStatus(status, statusText), result))) -// case Failure(e) => monad.pure(Left(FailedResult(FunctionStatus(status, statusText), e))) -// } -// } -// } -// } - } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala index 302d33ec..d019aca8 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala @@ -44,7 +44,3 @@ class DoobieQueryWithStatus[R](val fragment: Fragment)(implicit val readStatusWi override def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[R]): Either[StatusException, R] = checkStatus(statusWithData) } - -//class DoobieQueryWithStandardStatusHandling[R: Read](override val fragment: Fragment)(implicit override val readStatusWithDataR: Read[StatusWithData[R]]) -//// extends DoobieQueryWithStatus[A, B, R](fragment)(readStatusWithDataR) with StandardQueryStatusHandling -// extends DoobieQueryWithStatus[R](fragment)(readStatusWithDataR) with StandardQueryStatusHandling diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala b/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala index 637ba2af..93ec6dcf 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala @@ -23,6 +23,7 @@ import za.co.absa.fadb.DBFunction.DBSingleResultFunction import za.co.absa.fadb.DBSchema import com.github.tminglei.slickpg.{InetString, LTree, MacAddrString, Range} import org.scalatest.flatspec.AsyncFlatSpec +import za.co.absa.fadb.slick.SlickFunction.SlickSingleResultFunction import java.time.{Duration, LocalDate, LocalDateTime, LocalTime, OffsetDateTime, ZonedDateTime} import java.util.UUID @@ -33,8 +34,6 @@ class FaDbPostgresProfileSuite extends AsyncFlatSpec { private val database = Database.forConfig("postgrestestdb") private val testDBEngine: SlickPgEngine = new SlickPgEngine(database) - - behavior of "FaDbPostgresProfile" it should "be able to pass through and extract extended Postgres types" in { @@ -53,9 +52,8 @@ class FaDbPostgresProfileSuite extends AsyncFlatSpec { macaddr1: MacAddrString //macaddr ) - class TestFunction(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) - extends DBSingleResultFunction[InputOutput, InputOutput, SlickPgEngine, Future] - with SlickFunction[InputOutput, InputOutput] { + class TestFunction(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) + extends SlickSingleResultFunction[InputOutput, InputOutput] { override protected def sql(values: InputOutput): SQLActionBuilder = { sql"""SELECT #$selectEntry @@ -136,9 +134,8 @@ class FaDbPostgresProfileSuite extends AsyncFlatSpec { macaddr1: Option[MacAddrString] //macaddr ) - class TestFunction(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) - extends DBSingleResultFunction[InputOutput, InputOutput, SlickPgEngine, Future] - with SlickFunction[InputOutput, InputOutput] { + class TestFunction(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) + extends SlickSingleResultFunction[InputOutput, InputOutput] { override protected def sql(values: InputOutput): SQLActionBuilder = { sql"""SELECT #$selectEntry @@ -179,7 +176,6 @@ class FaDbPostgresProfileSuite extends AsyncFlatSpec { val testFunction = new TestFunction } - val inputOutput = InputOutput( None, None, diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala index c1682678..901834b2 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala @@ -21,6 +21,7 @@ import slick.jdbc.SQLActionBuilder import za.co.absa.fadb.DBFunction.DBMultipleResultFunction import za.co.absa.fadb.DBSchema import za.co.absa.fadb.slick.FaDbPostgresProfile.api._ +import za.co.absa.fadb.slick.SlickFunction.SlickMultipleResultFunction import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration.DurationInt @@ -29,9 +30,7 @@ import scala.concurrent.{Await, Future} class SlickMultipleResultFunctionTest extends AnyFunSuite with SlickTest { class GetActors(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) - extends DBMultipleResultFunction[GetActorsQueryParameters, Actor, SlickPgEngine, Future] - with SlickFunction[GetActorsQueryParameters, Actor] - with ActorSlickConverter { + extends SlickMultipleResultFunction[GetActorsQueryParameters, Actor] with ActorSlickConverter { override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("actor_id", "first_name", "last_name") diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala index be56affd..90487157 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala @@ -16,22 +16,20 @@ package za.co.absa.fadb.slick -import scala.concurrent.ExecutionContext.Implicits.global import org.scalatest.funsuite.AnyFunSuite import slick.jdbc.SQLActionBuilder -import za.co.absa.fadb.DBFunction.DBOptionalResultFunction import za.co.absa.fadb.DBSchema import za.co.absa.fadb.slick.FaDbPostgresProfile.api._ +import za.co.absa.fadb.slick.SlickFunction.SlickOptionalResultFunction -import scala.concurrent.{Await, Future} +import scala.concurrent.Await +import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration.DurationInt class SlickOptionalResultFunctionTest extends AnyFunSuite with SlickTest { class GetActorById(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) - extends DBOptionalResultFunction[Int, Actor, SlickPgEngine, Future] - with SlickFunction[Int, Actor] - with ActorSlickConverter { + extends SlickOptionalResultFunction[Int, Actor] with ActorSlickConverter { override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("actor_id", "first_name", "last_name") diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala new file mode 100644 index 00000000..1890f490 --- /dev/null +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala @@ -0,0 +1,35 @@ +package za.co.absa.fadb.slick + +import org.scalatest.funsuite.AnyFunSuite +import slick.jdbc.{GetResult, SQLActionBuilder} +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.slick.SlickFunction.SlickSingleResultFunctionWithStatus +import za.co.absa.fadb.slick.FaDbPostgresProfile.api._ + +import scala.concurrent.Await +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration.DurationInt + +class SlickSingleResultFunctionWithStatusTest extends AnyFunSuite with SlickTest { + class CreateActor(implicit schema: DBSchema, dbEngine: SlickPgEngine) + extends SlickSingleResultFunctionWithStatus[CreateActorRequestBody, Int] { + + override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("o_actor_id") + + override protected def sql(values: CreateActorRequestBody): SQLActionBuilder = + sql"""SELECT #$selectEntry FROM #$functionName(${values.firstName},${values.lastName}) #$alias;""" + + /** + * The `GetResult[R]` instance used to read the query result into `R`. + */ + override protected def slickConverter: GetResult[Int] = GetResult(r => r.<<) + } + + private val createActor = new CreateActor()(Runs, new SlickPgEngine(db)) + + test("SlickTest with status handling") { + val requestBody = CreateActorRequestBody("Pavel", "Marek") + val result = createActor(requestBody) + assert(Await.result(result, 5.seconds).isRight) + } +} diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickTest.scala index c9f8575b..01547a48 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickTest.scala @@ -20,6 +20,7 @@ import slick.jdbc.JdbcBackend.Database import za.co.absa.fadb.DBSchema trait SlickTest { + case class CreateActorRequestBody(firstName: String, lastName: String) case class GetActorsQueryParameters(firstName: Option[String], lastName: Option[String]) import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala index 06933c95..6a762290 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala @@ -16,45 +16,42 @@ package za.co.absa.fadb.slick +import cats.implicits._ import slick.jdbc.{GetResult, SQLActionBuilder} -import za.co.absa.fadb.DBFunctionFabric +import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBOptionalResultFunction, DBSingleResultFunction} +import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema} -/** - * Mix-in trait to use [[za.co.absa.fadb.DBFunction DBFunction]] with [[SlickPgEngine]]. Implements the abstract function `query` - * @tparam I - The input type of the function - * @tparam R - The return type of the function - */ -trait SlickFunction[I, R] extends DBFunctionFabric { +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future + +private[slick] trait SlickFunctionBase[I, R] { /** - * A reference to the [[SlickPgEngine]] to use the [[za.co.absa.fadb.DBFunction DBFunction]] with - */ - implicit val dbEngine: SlickPgEngine + * The `GetResult[R]` instance used to read the query result into `R`. + */ + protected def slickConverter: GetResult[R] /** - * This is expected to return SQL part of the [[SlickQuery]] (eventually returned by the `SlickPgFunction.query` function - * @param values - the values to pass over to the database function - * @return - the Slick representation of the SQL - */ + * Generates a Slick `SQLActionBuilder` representing the SQL query for the function. + * + * @param values the input values for the function + * @return the Slick `SQLActionBuilder` representing the SQL query + */ protected def sql(values: I): SQLActionBuilder - /** - * This is expected to return a method to convert the [[slick.jdbc.PositionedResult slick.PositionedResult]], the Slick general SQL result - * format into the `R` type - * @return - the converting function - */ - protected def slickConverter: GetResult[R] + def fieldsToSelect: Seq[String] /** - * Alias to use within the SQL query - */ + * Alias to use within the SQL query + */ protected val alias = "FNC" /** - * Helper function to use in the actual DB function class - * @return the SELECT part of the function call SQL query - */ - protected def selectEntry: String = { // TODO Not suggested to use until #6 will be implemented + * Helper function to use in the actual DB function class + * + * @return the SELECT part of the function call SQL query + */ + protected def selectEntry: String = { val fieldsSeq = fieldsToSelect if (fieldsSeq.isEmpty) { "*" @@ -67,13 +64,53 @@ trait SlickFunction[I, R] extends DBFunctionFabric { fieldsToSelect.map(aliasToUse + _).mkString(",") } } +} + +private[slick] trait SlickFunction[I, R] extends SlickFunctionBase[I, R] { /** - * This mix-in main reason of existence. It implements the `query` function for [[za.co.absa.fadb.DBFunction DBFunction]] for [[SlickPgEngine]] - * @param values - the values to pass over to the database function - * @return - the SQL query in [[SlickQuery]] form - */ - protected def query(values: I): dbEngine.QueryType[R] = { - new SlickQuery(sql(values), slickConverter) - } + * Generates a `SlickQuery[R]` representing the SQL query for the function. + * + * @param values the input values for the function + * @return the `SlickQuery[R]` representing the SQL query + */ + protected def query(values: I): SlickQuery[R] = new SlickQuery(sql(values), slickConverter) +} + +private[slick] trait SlickFunctionWithStatus[I, R] extends SlickFunctionBase[I, R] { + + /** + * Generates a `SlickQueryWithStatus[R]` representing the SQL query for the function with status support. + * + * @param status - the status to check + * @return - Success or failure the status means + */ + protected def query(values: I): SlickQueryWithStatus[R] = new SlickQueryWithStatus[R](sql(values), slickConverter) +} + +object SlickFunction { + + abstract class SlickSingleResultFunctionWithStatus[I, R](functionNameOverride: Option[String] = None)( + implicit override val schema: DBSchema, + DBEngine: SlickPgEngine + ) extends DBFunctionWithStatus[I, R, SlickPgEngine, Future](functionNameOverride) + with SlickFunctionWithStatus[I, R] + + abstract class SlickSingleResultFunction[I, R](functionNameOverride: Option[String] = None)( + implicit override val schema: DBSchema, + DBEngine: SlickPgEngine + ) extends DBSingleResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) + with SlickFunction[I, R] + + abstract class SlickMultipleResultFunction[I, R](functionNameOverride: Option[String] = None)( + implicit override val schema: DBSchema, + DBEngine: SlickPgEngine + ) extends DBMultipleResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) + with SlickFunction[I, R] + + abstract class SlickOptionalResultFunction[I, R](functionNameOverride: Option[String] = None)( + implicit override val schema: DBSchema, + DBEngine: SlickPgEngine + ) extends DBOptionalResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) + with SlickFunction[I, R] } diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunctionWithStatusSupport.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunctionWithStatusSupport.scala index ab6a0d54..86e43553 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunctionWithStatusSupport.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunctionWithStatusSupport.scala @@ -1,66 +1,66 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb.slick - -import slick.jdbc.{GetResult, PositionedResult} -import za.co.absa.fadb.status.FunctionStatus - -import scala.util.Try - -/** - * An extension of the [[SlickFunction]] mix-in trait to add support of status handling - * This trait expects another mix-in of [[za.co.absa.fadb.status.handling.StatusHandling StatusHandling]] (or implementation of `checkStatus` function) - * - * @tparam I - The input type of the function - * @tparam R - The return type of the function - */ -trait SlickFunctionWithStatusSupport[I, R] extends SlickFunction[I, R] { - - /** - * Function which should actually check the status code returned by the DB function. Expected to got implemented by - * [[za.co.absa.fadb.status.handling.StatusHandling StatusHandling]] successor trait. But of course can be implemented directly. - * - * @param status - the status to check - * @return - Success or failure the status means - */ - protected def checkStatus(status: FunctionStatus): Try[FunctionStatus] - - /** - * A special extension of the converting function that first picks up status code and status check and checks for their - * meaning. Then the original conversion is executed. - * @param queryResult - the result of the SQL query, the input of the original converting function - * @param actualConverter - the original converting function - * @return - new converting function that also checks for status - */ - private def converterWithStatus(queryResult: PositionedResult, actualConverter: GetResult[R]): R = { - val status:Int = queryResult.<< - val statusText: String = queryResult.<< - checkStatus(FunctionStatus(status, statusText)).get //throw exception if status was off - actualConverter(queryResult) - } - - /** - * Replaces the converter with one that also extracts and checks status code and status text. - * @param values - the values to pass over to the database function - * @return - the SQL query in [[SlickQuery]] form - */ - override protected def query(values: I): dbEngine.QueryType[R] = { - val original = super.query(values) - new SlickQuery[R](original.sql, GetResult{converterWithStatus(_, original.getResult)}) - } - -} +///* +// * Copyright 2022 ABSA Group Limited +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package za.co.absa.fadb.slick +// +//import slick.jdbc.{GetResult, PositionedResult} +//import za.co.absa.fadb.status.FunctionStatus +// +//import scala.util.Try +// +///** +// * An extension of the [[SlickFunction]] mix-in trait to add support of status handling +// * This trait expects another mix-in of [[za.co.absa.fadb.status.handling.StatusHandling StatusHandling]] (or implementation of `checkStatus` function) +// * +// * @tparam I - The input type of the function +// * @tparam R - The return type of the function +// */ +//trait SlickFunctionWithStatusSupport[I, R] extends SlickFunction[I, R] { +// +// /** +// * Function which should actually check the status code returned by the DB function. Expected to got implemented by +// * [[za.co.absa.fadb.status.handling.StatusHandling StatusHandling]] successor trait. But of course can be implemented directly. +// * +// * @param status - the status to check +// * @return - Success or failure the status means +// */ +// protected def checkStatus(status: FunctionStatus): Try[FunctionStatus] +// +// /** +// * A special extension of the converting function that first picks up status code and status check and checks for their +// * meaning. Then the original conversion is executed. +// * @param queryResult - the result of the SQL query, the input of the original converting function +// * @param actualConverter - the original converting function +// * @return - new converting function that also checks for status +// */ +// private def converterWithStatus(queryResult: PositionedResult, actualConverter: GetResult[R]): R = { +// val status:Int = queryResult.<< +// val statusText: String = queryResult.<< +// checkStatus(FunctionStatus(status, statusText)).get //throw exception if status was off +// actualConverter(queryResult) +// } +// +// /** +// * Replaces the converter with one that also extracts and checks status code and status text. +// * @param values - the values to pass over to the database function +// * @return - the SQL query in [[SlickQuery]] form +// */ +// override protected def query(values: I): dbEngine.QueryType[R] = { +// val original = super.query(values) +// new SlickQuery[R](original.sql, GetResult{converterWithStatus(_, original.getResult)}) +// } +// +//} diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala index 9a54a618..f1b3d8d2 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala @@ -54,7 +54,7 @@ class SlickPgEngine(val db: Database)(implicit val executor: ExecutionContext) e } // override def fetchHeadWithStatusHandling[_, _, R](query: SlickQueryWithStatus[R]): Future[Either[StatusException, R]] = { - override def fetchHeadWithStatusHandling[R](query: QueryWithStatusType[R]): Future[Either[StatusException, R]] = { + override def fetchHeadWithStatus[R](query: QueryWithStatusType[R]): Future[Either[StatusException, R]] = { implicit val getPositionedResult: GetResult[PositionedResult] = GetResult(r => r) db.run(query.sql.as[PositionedResult]).map { results => query.getResultOrException(results.head) diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala index c6aa1548..a84d4f04 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala @@ -17,6 +17,7 @@ package za.co.absa.fadb.slick import slick.jdbc.{GetResult, PositionedResult, SQLActionBuilder} +import za.co.absa.fadb.status.handling.StandardQueryStatusHandling import za.co.absa.fadb.status.{FunctionStatus, StatusException} import za.co.absa.fadb.{FunctionStatusWithData, Query, QueryWithStatus} @@ -30,8 +31,8 @@ import za.co.absa.fadb.{FunctionStatusWithData, Query, QueryWithStatus} class SlickQuery[R](val sql: SQLActionBuilder, val getResult: GetResult[R]) extends Query[R] // QueryStatusHandling has to be mixed-in for the checkStatus method implementation -abstract class SlickQueryWithStatus[R](val sql: SQLActionBuilder, val getResult: GetResult[R]) - extends QueryWithStatus[PositionedResult, PositionedResult, R] { +class SlickQueryWithStatus[R](val sql: SQLActionBuilder, val getResult: GetResult[R]) + extends QueryWithStatus[PositionedResult, PositionedResult, R] with StandardQueryStatusHandling { override def processStatus(initialResult: PositionedResult): FunctionStatusWithData[PositionedResult] = { val status: Int = initialResult.<< From bcd8d75391e4fddbb46af6af918151bd0aa69ef2 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 7 Dec 2023 22:37:39 +0100 Subject: [PATCH 23/90] first working version --- core/src/main/scala/za/co/absa/fadb/DBFunction.scala | 2 ++ core/src/main/scala/za/co/absa/fadb/Query.scala | 1 + .../SlickSingleResultFunctionWithStatusTest.scala | 1 + .../scala/za/co/absa/fadb/slick/SlickPgEngine.scala | 10 +++++----- .../main/scala/za/co/absa/fadb/slick/SlickQuery.scala | 4 ++++ 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 67b2759f..19021a4f 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -75,6 +75,8 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functio protected def query(values: I): dBEngine.QueryWithStatusType[R] def apply(values: I): F[Either[StatusException, R]] = dBEngine.fetchHeadWithStatus(query(values)) + + override def fieldsToSelect: Seq[String] = Seq("status", "status_text") ++ super.fieldsToSelect } object DBFunction { diff --git a/core/src/main/scala/za/co/absa/fadb/Query.scala b/core/src/main/scala/za/co/absa/fadb/Query.scala index b9069612..abc796fd 100644 --- a/core/src/main/scala/za/co/absa/fadb/Query.scala +++ b/core/src/main/scala/za/co/absa/fadb/Query.scala @@ -31,4 +31,5 @@ trait QueryWithStatus[A, B, R] extends QueryStatusHandling { def processStatus(initialResult: A): FunctionStatusWithData[B] def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[B]): Either[StatusException, R] def getResultOrException(initialResult: A): Either[StatusException, R] = toStatusExceptionOrData(processStatus(initialResult)) +// def getResultOrException(initialResult: Vector[A]): Either[StatusException, R] = initialResult.map(getResultOrException).head } diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala index 1890f490..f7110dc7 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala @@ -31,5 +31,6 @@ class SlickSingleResultFunctionWithStatusTest extends AnyFunSuite with SlickTest val requestBody = CreateActorRequestBody("Pavel", "Marek") val result = createActor(requestBody) assert(Await.result(result, 5.seconds).isRight) + println(result) } } diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala index f1b3d8d2..61becc9f 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala @@ -53,11 +53,11 @@ class SlickPgEngine(val db: Database)(implicit val executor: ExecutionContext) e db.run(slickAction) } -// override def fetchHeadWithStatusHandling[_, _, R](query: SlickQueryWithStatus[R]): Future[Either[StatusException, R]] = { override def fetchHeadWithStatus[R](query: QueryWithStatusType[R]): Future[Either[StatusException, R]] = { - implicit val getPositionedResult: GetResult[PositionedResult] = GetResult(r => r) - db.run(query.sql.as[PositionedResult]).map { results => - query.getResultOrException(results.head) - } +// implicit val getPositionedResult: GetResult[PositionedResult] = GetResult(r => r) +// val slickAction = query.sql.as[PositionedResult].head.map(query.getResultOrException) +// val slickAction = query.sql.as[PositionedResult].head.map(query.test(query.getResult)) + val slickAction = query.sql.as[Either[StatusException, R]](query.getStatusExceptionOrData).head + db.run(slickAction) } } diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala index a84d4f04..6e126272 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala @@ -46,4 +46,8 @@ class SlickQueryWithStatus[R](val sql: SQLActionBuilder, val getResult: GetResul case Right(value) => Right(getResult(value)) } } + + def getStatusExceptionOrData: GetResult[Either[StatusException, R]] = { + GetResult(pr => processStatus(pr)).andThen(fs => toStatusExceptionOrData(fs)) + } } From 3a1f7409c2bb831d79750976d053a5361346a20c Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 8 Dec 2023 11:08:44 +0100 Subject: [PATCH 24/90] working version with status handling available to be defined by clients and mixed into db functions with status --- .../main/scala/za/co/absa/fadb/DBEngine.scala | 63 +++--- .../scala/za/co/absa/fadb/DBFunction.scala | 182 +++++++++-------- .../za/co/absa/fadb/DBFunctionFabric.scala | 19 +- .../main/scala/za/co/absa/fadb/DBSchema.scala | 25 ++- .../co/absa/fadb/FunctionStatusWithData.scala | 2 +- .../main/scala/za/co/absa/fadb/Query.scala | 17 +- .../StatusException.scala | 18 +- .../absa/fadb/naming/NamingConvention.scala | 2 +- .../naming/implementations/AsIsNaming.scala | 2 +- .../implementations/SnakeCaseNaming.scala | 6 +- .../co/absa/fadb/status/FunctionStatus.scala | 8 +- .../scala/za/co/absa/fadb/status/README.md | 29 +-- .../status/handling/QueryStatusHandling.scala | 19 +- .../fadb/status/handling/StatusHandling.scala | 63 ------ .../StandardQueryStatusHandling.scala | 21 ++ .../SnakeCaseNamingSuite.scala | 1 - .../fadb/status/StatusExceptionSuite.scala | 2 +- .../doobie/DoobieFunctionWithStatusTest.scala | 26 --- ...ieSingleResultFunctionWithStatusTest.scala | 5 +- .../za/co/absa/fadb/doobie/DoobieEngine.scala | 7 +- .../co/absa/fadb/doobie/DoobieFunction.scala | 24 ++- .../za/co/absa/fadb/doobie/DoobieQuery.scala | 22 +-- .../za/co/absa/fadb/doobie/Results.scala | 44 ----- .../co/absa/fadb/doobie/StatusWithData.scala | 3 + .../enceladus/DatasetSchemaSuite.scala | 2 +- .../fadb/slick/FaDbPostgresProfileSuite.scala | 187 +++++++++--------- .../SlickMultipleResultFunctionTest.scala | 3 +- .../SlickOptionalResultFunctionTest.scala | 3 +- ...ckSingleResultFunctionWithStatusTest.scala | 7 +- .../absa/fadb/slick/FaDbPostgresProfile.scala | 39 ++-- .../za/co/absa/fadb/slick/SlickFunction.scala | 30 +-- .../SlickFunctionWithStatusSupport.scala | 66 ------- .../za/co/absa/fadb/slick/SlickPgEngine.scala | 25 +-- .../za/co/absa/fadb/slick/SlickQuery.scala | 27 +-- .../fadb/slick/support/PgUUIDSupport.scala | 14 +- 35 files changed, 424 insertions(+), 589 deletions(-) rename core/src/main/scala/za/co/absa/fadb/{status => exceptions}/StatusException.scala (54%) delete mode 100644 core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala create mode 100644 core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala delete mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusTest.scala delete mode 100644 doobie/src/main/scala/za/co/absa/fadb/doobie/Results.scala create mode 100644 doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala delete mode 100644 slick/src/main/scala/za/co/absa/fadb/slick/SlickFunctionWithStatusSupport.scala diff --git a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala index 90ba3ea6..0928d351 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala @@ -18,65 +18,64 @@ package za.co.absa.fadb import cats.Monad import cats.implicits.toFunctorOps -import za.co.absa.fadb.status.StatusException +import za.co.absa.fadb.exceptions.StatusException import scala.language.higherKinds /** - * A basis to represent a database executor - */ + * A basis to represent a database executor + */ abstract class DBEngine[F[_]: Monad] { /** - * A type representing the (SQL) query within the engine - * @tparam T - the return type of the query - */ + * A type representing the (SQL) query within the engine + * @tparam T - the return type of the query + */ type QueryType[T] <: Query[T] type QueryWithStatusType[R] <: QueryWithStatus[_, _, R] /** - * The actual query executioner of the queries of the engine - * @param query - the query to execute - * @tparam R - return the of the query - * @return - sequence of the results of database query - */ + * The actual query executioner of the queries of the engine + * @param query - the query to execute + * @tparam R - return the of the query + * @return - sequence of the results of database query + */ protected def run[R](query: QueryType[R]): F[Seq[R]] /** - * The actual query executioner of the queries of the engine - * @param query - the query to execute - * @tparam R - return the of the query - * @return - sequence of the results of database query - */ + * The actual query executioner of the queries of the engine + * @param query - the query to execute + * @tparam R - return the of the query + * @return - sequence of the results of database query + */ def fetchHeadWithStatus[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] /** - * Public method to execute when query is expected to return multiple results - * @param query - the query to execute - * @tparam R - return the of the query - * @return - sequence of the results of database query - */ + * Public method to execute when query is expected to return multiple results + * @param query - the query to execute + * @tparam R - return the of the query + * @return - sequence of the results of database query + */ def fetchAll[R](query: QueryType[R]): F[Seq[R]] = run(query) /** - * Public method to execute when query is expected to return exactly one row - * @param query - the query to execute - * @tparam R - return the of the query - * @return - sequence of the results of database query - */ + * Public method to execute when query is expected to return exactly one row + * @param query - the query to execute + * @tparam R - return the of the query + * @return - sequence of the results of database query + */ def fetchHead[R](query: QueryType[R]): F[R] = { run(query).map(_.head) } /** - * Public method to execute when query is expected to return one or no results - * @param query - the query to execute - * @tparam R - return the of the query - * @return - sequence of the results of database query - */ + * Public method to execute when query is expected to return one or no results + * @param query - the query to execute + * @tparam R - return the of the query + * @return - sequence of the results of database query + */ def fetchHeadOption[R](query: QueryType[R]): F[Option[R]] = { run(query).map(_.headOption) } } - diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 19021a4f..80a2c395 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -17,24 +17,25 @@ package za.co.absa.fadb import cats.Monad -import za.co.absa.fadb.status.StatusException +import za.co.absa.fadb.exceptions.StatusException +import za.co.absa.fadb.status.handling.QueryStatusHandling import scala.language.higherKinds /** - * - * @param functionNameOverride - in case the class name would not match the database function name, this gives the - * possibility of override - * @param schema - the schema the function belongs into - * @param dBEngine - the database engine that is supposed to execute the function (presumably contains - * connection to the database - * @tparam I - the type covering the input fields of the database function - * @tparam R - the type covering the returned fields from the database function - * @tparam E - the type of the [[DBEngine]] engine - */ -abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) - (implicit override val schema: DBSchema, val dBEngine: E) - extends DBFunctionFabric(functionNameOverride) { + * @param functionNameOverride - in case the class name would not match the database function name, this gives the + * possibility of override + * @param schema - the schema the function belongs into + * @param dBEngine - the database engine that is supposed to execute the function (presumably contains + * connection to the database + * @tparam I - the type covering the input fields of the database function + * @tparam R - the type covering the returned fields from the database function + * @tparam E - the type of the [[DBEngine]] engine + */ +abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None)(implicit + override val schema: DBSchema, + val dBEngine: E +) extends DBFunctionFabric(functionNameOverride) { // A constructor that takes only the mandatory parameters and uses default values for the optional ones def this()(implicit schema: DBSchema, dBEngine: E) = this(None) @@ -43,11 +44,11 @@ abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverr def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) /** - * Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the - * DBEngine specific mix-in. - * @param values - the values to pass over to the database function - * @return - the SQL query in the format specific to the provided [[DBEngine]] - */ + * Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the + * DBEngine specific mix-in. + * @param values - the values to pass over to the database function + * @return - the SQL query in the format specific to the provided [[DBEngine]] + */ protected def query(values: I): dBEngine.QueryType[R] /*these 3 functions has to be defined here and not in the ancestors, as there the query type is not compatible - path-dependent types*/ @@ -56,9 +57,12 @@ abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverr protected def optionalResult(values: I): F[Option[R]] = dBEngine.fetchHeadOption(query(values)) } -abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) - (implicit override val schema: DBSchema, val dBEngine: E) - extends DBFunctionFabric(functionNameOverride) { +abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None)( + implicit + override val schema: DBSchema, + val dBEngine: E +) extends DBFunctionFabric(functionNameOverride) + with QueryStatusHandling { // A constructor that takes only the mandatory parameters and uses default values for the optional ones def this()(implicit schema: DBSchema, dBEngine: E) = this(None) @@ -67,33 +71,51 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functio def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) /** - * Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the - * DBEngine specific mix-in. - * @param values - the values to pass over to the database function - * @return - the SQL query in the format specific to the provided [[DBEngine]] + * Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the + * DBEngine specific mix-in. + * @param values - the values to pass over to the database function + * @return - the SQL query in the format specific to the provided [[DBEngine]] */ protected def query(values: I): dBEngine.QueryWithStatusType[R] def apply(values: I): F[Either[StatusException, R]] = dBEngine.fetchHeadWithStatus(query(values)) - override def fieldsToSelect: Seq[String] = Seq("status", "status_text") ++ super.fieldsToSelect + val defaultStatusField = "status" + val defaultStatusTextField = "statusText" + + /** + * A mix-in to add the status fields into the SELECT statement + * + * @return a sequence of fields to use in SELECT + */ + override def fieldsToSelect: Seq[String] = { + Seq( + schema.namingConvention.stringPerConvention(defaultStatusField), + schema.namingConvention.stringPerConvention(defaultStatusTextField) + ) ++ super.fieldsToSelect + } + + // implementation to be mixed in + override def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] } object DBFunction { + /** - * Represents a function returning a set (in DB sense) of rows - * @param functionNameOverride - in case the class name would not match the database function name, this gives the - * possibility of override - * @param schema - the schema the function belongs into - * @param dBEngine - the database engine that is supposed to execute the function (presumably contains - * connection to the database - * @tparam I - the type covering the input fields of the database function - * @tparam R - the type covering the returned fields from the database function - * @tparam E - the type of the [[DBEngine]] engine - */ - abstract class DBMultipleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) - (implicit schema: DBSchema, dBEngine: E) - extends DBFunction[I, R, E, F](functionNameOverride) { + * Represents a function returning a set (in DB sense) of rows + * @param functionNameOverride - in case the class name would not match the database function name, this gives the + * possibility of override + * @param schema - the schema the function belongs into + * @param dBEngine - the database engine that is supposed to execute the function (presumably contains + * connection to the database + * @tparam I - the type covering the input fields of the database function + * @tparam R - the type covering the returned fields from the database function + * @tparam E - the type of the [[DBEngine]] engine + */ + abstract class DBMultipleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( + functionNameOverride: Option[String] = None + )(implicit schema: DBSchema, dBEngine: E) + extends DBFunction[I, R, E, F](functionNameOverride) { // A constructor that takes only the mandatory parameters and uses default values for the optional ones def this()(implicit schema: DBSchema, dBEngine: E) = this(None) @@ -102,28 +124,29 @@ object DBFunction { def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) /** - * For easy and convenient execution of the DB function call - * @param values - the values to pass over to the database function - * @return - a sequence of values, each coming from a row returned from the DB function transformed to scala - * type `R` - */ + * For easy and convenient execution of the DB function call + * @param values - the values to pass over to the database function + * @return - a sequence of values, each coming from a row returned from the DB function transformed to scala + * type `R` + */ def apply(values: I): F[Seq[R]] = multipleResults(values) } /** - * Represents a function returning exactly one record - * @param functionNameOverride - in case the class name would not match the database function name, this gives the - * possibility of override - * @param schema - the schema the function belongs into - * @param dBEngine - the database engine that is supposed to execute the function (presumably contains - * connection to the database - * @tparam I - the type covering the input fields of the database function - * @tparam R - the type covering the returned fields from the database function - * @tparam E - the type of the [[DBEngine]] engine - */ - abstract class DBSingleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) - (implicit schema: DBSchema, dBEngine: E) - extends DBFunction[I, R, E, F](functionNameOverride) { + * Represents a function returning exactly one record + * @param functionNameOverride - in case the class name would not match the database function name, this gives the + * possibility of override + * @param schema - the schema the function belongs into + * @param dBEngine - the database engine that is supposed to execute the function (presumably contains + * connection to the database + * @tparam I - the type covering the input fields of the database function + * @tparam R - the type covering the returned fields from the database function + * @tparam E - the type of the [[DBEngine]] engine + */ + abstract class DBSingleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( + functionNameOverride: Option[String] = None + )(implicit schema: DBSchema, dBEngine: E) + extends DBFunction[I, R, E, F](functionNameOverride) { // A constructor that takes only the mandatory parameters and uses default values for the optional ones def this()(implicit schema: DBSchema, dBEngine: E) = this(None) @@ -132,27 +155,28 @@ object DBFunction { def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) /** - * For easy and convenient execution of the DB function call - * @param values - the values to pass over to the database function - * @return - the value returned from the DB function transformed to scala type `R` - */ + * For easy and convenient execution of the DB function call + * @param values - the values to pass over to the database function + * @return - the value returned from the DB function transformed to scala type `R` + */ def apply(values: I): F[R] = singleResult(values) } /** - * Represents a function returning one optional record - * @param functionNameOverride - in case the class name would not match the database function name, this gives the - * possibility of override - * @param schema - the schema the function belongs into - * @param dBEngine - the database engine that is supposed to execute the function (presumably contains - * connection to the database - * @tparam I - the type covering the input fields of the database function - * @tparam R - the type covering the returned fields from the database function - * @tparam E - the type of the [[DBEngine]] engine - */ - abstract class DBOptionalResultFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None) - (implicit schema: DBSchema, dBEngine: E) - extends DBFunction[I, R, E, F](functionNameOverride) { + * Represents a function returning one optional record + * @param functionNameOverride - in case the class name would not match the database function name, this gives the + * possibility of override + * @param schema - the schema the function belongs into + * @param dBEngine - the database engine that is supposed to execute the function (presumably contains + * connection to the database + * @tparam I - the type covering the input fields of the database function + * @tparam R - the type covering the returned fields from the database function + * @tparam E - the type of the [[DBEngine]] engine + */ + abstract class DBOptionalResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( + functionNameOverride: Option[String] = None + )(implicit schema: DBSchema, dBEngine: E) + extends DBFunction[I, R, E, F](functionNameOverride) { // A constructor that takes only the mandatory parameters and uses default values for the optional ones def this()(implicit schema: DBSchema, dBEngine: E) = this(None) @@ -161,10 +185,10 @@ object DBFunction { def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) /** - * For easy and convenient execution of the DB function call - * @param values - the values to pass over to the database function - * @return - the value returned from the DB function transformed to scala type `R` if a row is returned, otherwise `None` - */ + * For easy and convenient execution of the DB function call + * @param values - the values to pass over to the database function + * @return - the value returned from the DB function transformed to scala type `R` if a row is returned, otherwise `None` + */ def apply(values: I): F[Option[R]] = optionalResult(values) } } diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala b/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala index 23d0503c..bb607e51 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala @@ -16,22 +16,20 @@ package za.co.absa.fadb -import za.co.absa.fadb.naming.NamingConvention - /** - * This trait serves the purpose of introducing functions that are common to all DB Function objects and mix-in traits - * that offer certain implementations. This trait should help with the inheritance of all of these - */ + * This trait serves the purpose of introducing functions that are common to all DB Function objects and mix-in traits + * that offer certain implementations. This trait should help with the inheritance of all of these + */ abstract class DBFunctionFabric(functionNameOverride: Option[String])(implicit val schema: DBSchema) { - /** - * List of fields to select from the DB function. - * @return - list of fields to select - */ + /** + * List of fields to select from the DB function. + * @return - list of fields to select + */ def fieldsToSelect: Seq[String] = Seq.empty /** - * Name of the function, based on the class name, unless it is overridden in the constructor + * Name of the function, based on the class name, unless it is overridden in the constructor */ val functionName: String = { val fn = functionNameOverride.getOrElse(schema.objectNameFromClassName(getClass)) @@ -42,5 +40,4 @@ abstract class DBFunctionFabric(functionNameOverride: Option[String])(implicit v } } -// def namingConvention: NamingConvention = schema.namingConvention } diff --git a/core/src/main/scala/za/co/absa/fadb/DBSchema.scala b/core/src/main/scala/za/co/absa/fadb/DBSchema.scala index bf2933ed..30553300 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBSchema.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBSchema.scala @@ -19,14 +19,13 @@ package za.co.absa.fadb import za.co.absa.fadb.naming.NamingConvention /** - * An abstract class, an ancestor to represent a database schema - * The database name of the schema is derived from the class name based on the provided naming convention - * @param schemaNameOverride - in case the class name would not match the database schema name, this gives the - * @param namingConvention - the [[za.co.absa.fadb.naming.NamingConvention NamingConvention]] + * An abstract class, an ancestor to represent a database schema + * The database name of the schema is derived from the class name based on the provided naming convention + * @param schemaNameOverride - in case the class name would not match the database schema name, this gives the + * @param namingConvention - the [[za.co.absa.fadb.naming.NamingConvention NamingConvention]] * prescribing how to convert a class name into a db object name - */ -abstract class DBSchema(schemaNameOverride: Option[String] = None)(implicit val namingConvention: NamingConvention) -{ + */ +abstract class DBSchema(schemaNameOverride: Option[String] = None)(implicit val namingConvention: NamingConvention) { def this(schemaNameOverride: String)(implicit namingConvention: NamingConvention) { this(Option(schemaNameOverride))(namingConvention) @@ -41,22 +40,22 @@ abstract class DBSchema(schemaNameOverride: Option[String] = None)(implicit val } /** - * To easy pass over to [[DBFunction]] members of the schema + * To easy pass over to [[DBFunction]] members of the schema */ protected implicit val schema: DBSchema = this /** - * Function to convert a class to the associated DB object name, based on the class' name. For transformation from the - * class name to usual db name the schema's [[za.co.absa.fadb.naming.NamingConvention NamingConvention]] is used. - * @param c - class which name to use to get the DB object name - * @return - the db object name + * Function to convert a class to the associated DB object name, based on the class' name. For transformation from the + * class name to usual db name the schema's [[za.co.absa.fadb.naming.NamingConvention NamingConvention]] is used. + * @param c - class which name to use to get the DB object name + * @return - the db object name */ def objectNameFromClassName(c: Class[_]): String = { namingConvention.fromClassNamePerConvention(c) } /** - * Name of the schema. Based on the schema's class name or provided override + * Name of the schema. Based on the schema's class name or provided override */ val schemaName: String = schemaNameOverride.getOrElse(objectNameFromClassName(getClass)) diff --git a/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala b/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala index cb53b44b..db7f0533 100644 --- a/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala +++ b/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala @@ -2,4 +2,4 @@ package za.co.absa.fadb import za.co.absa.fadb.status.FunctionStatus -case class FunctionStatusWithData[A] (functionStatus: FunctionStatus, data: A) +case class FunctionStatusWithData[A](functionStatus: FunctionStatus, data: A) diff --git a/core/src/main/scala/za/co/absa/fadb/Query.scala b/core/src/main/scala/za/co/absa/fadb/Query.scala index abc796fd..87dc1475 100644 --- a/core/src/main/scala/za/co/absa/fadb/Query.scala +++ b/core/src/main/scala/za/co/absa/fadb/Query.scala @@ -16,20 +16,19 @@ package za.co.absa.fadb -import za.co.absa.fadb.status.StatusException -import za.co.absa.fadb.status.handling.QueryStatusHandling +import za.co.absa.fadb.exceptions.StatusException /** - * The basis for all query types of [[DBEngine]] implementations + * The basis for all query types of [[DBEngine]] implementations * - * @tparam R - the return type of the query - */ + * @tparam R - the return type of the query + */ trait Query[R] - -trait QueryWithStatus[A, B, R] extends QueryStatusHandling { +trait QueryWithStatus[A, B, R] { def processStatus(initialResult: A): FunctionStatusWithData[B] def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[B]): Either[StatusException, R] - def getResultOrException(initialResult: A): Either[StatusException, R] = toStatusExceptionOrData(processStatus(initialResult)) -// def getResultOrException(initialResult: Vector[A]): Either[StatusException, R] = initialResult.map(getResultOrException).head + def getResultOrException(initialResult: A): Either[StatusException, R] = toStatusExceptionOrData( + processStatus(initialResult) + ) } diff --git a/core/src/main/scala/za/co/absa/fadb/status/StatusException.scala b/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala similarity index 54% rename from core/src/main/scala/za/co/absa/fadb/status/StatusException.scala rename to core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala index 7105c673..d5272d7e 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/StatusException.scala +++ b/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala @@ -1,20 +1,6 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +package za.co.absa.fadb.exceptions -package za.co.absa.fadb.status +import za.co.absa.fadb.status.FunctionStatus sealed abstract class StatusException(val status: FunctionStatus) extends Exception(status.statusText) diff --git a/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala b/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala index 455ad4ff..5203585c 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala @@ -21,7 +21,7 @@ trait NamingConvention { val className = c.getSimpleName val cleanClassName = className.lastIndexOf('$') match { case -1 => className - case x => className.substring(0, x) + case x => className.substring(0, x) } stringPerConvention(cleanClassName) } diff --git a/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala b/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala index 73868ed8..6908174b 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala @@ -19,7 +19,7 @@ package za.co.absa.fadb.naming.implementations import za.co.absa.fadb.naming.{LettersCase, NamingConvention} import LettersCase.AsIs -class AsIsNaming(lettersCase: LettersCase) extends NamingConvention{ +class AsIsNaming(lettersCase: LettersCase) extends NamingConvention { override def stringPerConvention(original: String): String = { lettersCase.convert(original) } diff --git a/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala b/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala index 1b57194b..db2bdba7 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala @@ -27,7 +27,7 @@ class SnakeCaseNaming(lettersCase: LettersCase) extends NamingConvention { private def stripIfFirstChar(s: String, ch: Char): String = { if (s == "") { s - } else if (s(0) == ch){ + } else if (s(0) == ch) { s.substring(1) } else { s @@ -35,9 +35,7 @@ class SnakeCaseNaming(lettersCase: LettersCase) extends NamingConvention { } override def stringPerConvention(original: String): String = { - lettersCase.convert( - stripIfFirstChar( - camelCaseToSnakeCase(original), '_')) + lettersCase.convert(stripIfFirstChar(camelCaseToSnakeCase(original), '_')) } } diff --git a/core/src/main/scala/za/co/absa/fadb/status/FunctionStatus.scala b/core/src/main/scala/za/co/absa/fadb/status/FunctionStatus.scala index 3ac1ad71..b34ba93e 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/FunctionStatus.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/FunctionStatus.scala @@ -17,8 +17,8 @@ package za.co.absa.fadb.status /** - * Class represents the status of calling a fa-db function (if it supports status that is) - * @param statusCode - status code identifying if the function call succeeded or failed and how - * @param statusText - human readable description of the status returned - */ + * Class represents the status of calling a fa-db function (if it supports status that is) + * @param statusCode - status code identifying if the function call succeeded or failed and how + * @param statusText - human readable description of the status returned + */ case class FunctionStatus(statusCode: Int, statusText: String) diff --git a/core/src/main/scala/za/co/absa/fadb/status/README.md b/core/src/main/scala/za/co/absa/fadb/status/README.md index 74e6d6c0..1be08888 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/README.md +++ b/core/src/main/scala/za/co/absa/fadb/status/README.md @@ -1,25 +1,32 @@ # Intro -This is a list of convention status codes to return from functions - the primary usage intention is in Postgres DB +This is a list of convention status codes to return from functions - the primary usage intention is in Postgres DB functions, particularly in combination with fa-db library. The expected usage is that each function returns at least two values - `status` and `status_text`. -Status is one of the values below. Status text is a human readable explanation of what happened. Status text should not +Status is one of the values below. Status text is a human readable explanation of what happened. Status text should not have a syntactical meaning. Exceptions from the rule above: + * Immutable functions returning simple value -* Function returning a (large) set of values, basically a query, where the obvious result would be no records for _“not found”_, otherwise a general ok `status` and `status_text` on each line, just increasing the size of returned data. -* _“Private”_ functions (not callable from outside of DB (no grants), name starting with underscore), if it’s more convenient for the usage of the function. It’s still recommended to use the `status` and `status_text` even there. +* Function returning a (large) set of values, basically a query, where the obvious result would be no records for _“not + found”_, otherwise a general ok `status` and `status_text` on each line, just increasing the size of returned data. +* _“Private”_ functions (not callable from outside of DB (no grants), name starting with underscore), if it’s more + convenient for the usage of the function. It’s still recommended to use the `status` and `status_text` even there. General principle of status (code): + * The codes are two digit * They are divided into sections * There are fixed values with a preferred meaning, try to keep to it -* In each range there are “open” values to use in other or sub-cases of the general range meaning. Their meaning is specific the each function and depends on the contract between the function and the app calling it -* Function should list all the possible returned statuses in it’s header/inline documentation together with their used meaning -* When a status code suggests a not-OK state (values 20-99) the eventual other returned field values beyond `status` and `status_text` are undefined (probably will be NULL, but it should not be taken as a fact) +* In each range there are “open” values to use in other or sub-cases of the general range meaning. Their meaning is + specific the each function and depends on the contract between the function and the app calling it +* Function should list all the possible returned statuses in it’s header/inline documentation together with their used + meaning +* When a status code suggests a not-OK state (values 20-99) the eventual other returned field values beyond `status` + and `status_text` are undefined (probably will be NULL, but it should not be taken as a fact) * The possible returned status codes and their meaning should be described in the function comments # Codes @@ -30,7 +37,7 @@ The codes to be double digit ### 10-19 -| 10 | general ok | +| 10 | general ok | | 11 | data created | | 12 | data updated | | 14 | no op needed | @@ -58,8 +65,8 @@ Rest of the codes meaning depends on agreement in contact between DB and app ### 40-49 | 40 | requested data not found | -| 41 | master record not found | -| 42 | detail record not found | +| 41 | master record not found | +| 42 | detail record not found | Rest of the codes meaning depends on agreement in contact between DB and app @@ -69,7 +76,7 @@ Rest of the codes meaning depends on agreement in contact between DB and app | 50-59 | generally incorrect data (when obvious from context) | | 60-69 | missing value (usually NULL) | -| 70-79 | value out of range | +| 70-79 | value out of range | | 80-89 | wrong content of complex types (json, xml, hstore), like missing key, undesired key etc. | ## Free range for other errors diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala index e8833f5e..cd6c3bde 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala @@ -1,23 +1,8 @@ package za.co.absa.fadb.status.handling import za.co.absa.fadb.FunctionStatusWithData -import za.co.absa.fadb.status._ +import za.co.absa.fadb.exceptions.StatusException trait QueryStatusHandling { - protected def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] -} - -trait StandardQueryStatusHandling extends QueryStatusHandling { - override protected def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] = { - val functionStatus = statusWithData.functionStatus - functionStatus.statusCode / 10 match { - case 1 => Right(statusWithData.data) - case 2 => Left(ServerMisconfigurationException(functionStatus)) - case 3 => Left(DataConflictException(functionStatus)) - case 4 => Left(DataNotFoundException(functionStatus)) - case 5 | 6 | 7 | 8 => Left(ErrorInDataException(functionStatus)) - case 9 => Left(OtherStatusException(functionStatus)) - case _ => Left(StatusOutOfRangeException(functionStatus)) - } - } + def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] } diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala deleted file mode 100644 index 54a6dba5..00000000 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb.status.handling - -import za.co.absa.fadb.DBFunctionFabric -import za.co.absa.fadb.naming.NamingConvention -import za.co.absa.fadb.status.FunctionStatus -import za.co.absa.fadb.status.handling.StatusHandling.{defaultStatusField, defaultStatusTextField} - -import scala.util.Try - -/** - * A basis for mix-in traits for [[za.co.absa.fadb.DBFunction DBFunction]] that support `status` and `status text` for easier handling - */ -trait StatusHandling extends DBFunctionFabric { - - /** - * @return - the naming convention to use when converting the internal status and status text fields to DB fields - */ - def namingConvention: NamingConvention - - /** - * Verifies if the given status means success or failure - * @param status - the status to check - * @return - Success or failure the status means - */ - protected def checkStatus(status: FunctionStatus): Try[FunctionStatus] - protected def checkStatus(status: Integer, statusText: String): Try[FunctionStatus] = checkStatus(FunctionStatus(status, statusText)) - - def statusField: String = defaultStatusField - def statusTextField: String = defaultStatusTextField - - /** - * A mix-in to add the status fields into the SELECT statement - * @return a sequence of fields to use in SELECT - */ - override def fieldsToSelect: Seq[String] = { - Seq( - namingConvention.stringPerConvention(statusField), - namingConvention.stringPerConvention(statusTextField) - ) ++ super.fieldsToSelect - } - -} - -object StatusHandling { - val defaultStatusField = "status" - val defaultStatusTextField = "statusText" -} diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala new file mode 100644 index 00000000..70f17a42 --- /dev/null +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala @@ -0,0 +1,21 @@ +package za.co.absa.fadb.status.handling.implementations + +import za.co.absa.fadb.FunctionStatusWithData +import za.co.absa.fadb.exceptions.{DataConflictException, DataNotFoundException, ErrorInDataException, OtherStatusException, ServerMisconfigurationException, StatusException, StatusOutOfRangeException} +import za.co.absa.fadb.status._ +import za.co.absa.fadb.status.handling.QueryStatusHandling + +trait StandardQueryStatusHandling extends QueryStatusHandling { + override def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] = { + val functionStatus = statusWithData.functionStatus + functionStatus.statusCode / 10 match { + case 1 => Right(statusWithData.data) + case 2 => Left(ServerMisconfigurationException(functionStatus)) + case 3 => Left(DataConflictException(functionStatus)) + case 4 => Left(DataNotFoundException(functionStatus)) + case 5 | 6 | 7 | 8 => Left(ErrorInDataException(functionStatus)) + case 9 => Left(OtherStatusException(functionStatus)) + case _ => Left(StatusOutOfRangeException(functionStatus)) + } + } +} diff --git a/core/src/test/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNamingSuite.scala b/core/src/test/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNamingSuite.scala index 0a1a7be8..45e70692 100644 --- a/core/src/test/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNamingSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNamingSuite.scala @@ -8,7 +8,6 @@ class SnakeCaseNamingSuite extends AnyWordSpec with Matchers { private class ThisIsATestClass private val testInstance = new ThisIsATestClass() - "stringPerConvention" should { "handle empty string" in { val nm = new SnakeCaseNaming(AsIs) diff --git a/core/src/test/scala/za/co/absa/fadb/status/StatusExceptionSuite.scala b/core/src/test/scala/za/co/absa/fadb/status/StatusExceptionSuite.scala index 48f99bf1..501a3ae3 100644 --- a/core/src/test/scala/za/co/absa/fadb/status/StatusExceptionSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/status/StatusExceptionSuite.scala @@ -17,7 +17,7 @@ //package za.co.absa.fadb.status // //import org.scalatest.funsuite.AnyFunSuite -//import za.co.absa.fadb.status.StatusException._ +//import za.co.absa.fadb.exceptions.StatusException._ // //class StatusExceptionSuite extends AnyFunSuite { // test("Test equals - when they are the same") { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusTest.scala deleted file mode 100644 index 7c95df95..00000000 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieFunctionWithStatusTest.scala +++ /dev/null @@ -1,26 +0,0 @@ -package za.co.absa.fadb.doobie - -import cats.effect.IO -import cats.effect.unsafe.implicits.global -import doobie.implicits.toSqlInterpolator -import doobie.util.Read -import doobie.util.fragment.Fragment -import org.scalatest.funsuite.AnyFunSuite -import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStatus - -class DoobieFunctionWithStatusTest extends AnyFunSuite with DoobieTest { - class CreateActor(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) - extends DoobieSingleResultFunctionWithStatus[CreateActorRequestBody, Int, IO] { - - override def sql(values: CreateActorRequestBody)(implicit read: Read[StatusWithData[Int]]): Fragment = - sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" - } - - test("whatever") { - val createActor = new CreateActor()(Runs, new DoobieEngine(transactor)) - val result = createActor(CreateActorRequestBody("Pavel", "Marek")).unsafeRunSync() - println(result) - assert(result.isRight) - } -} diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala index 1fdb3c42..9addd436 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala @@ -24,11 +24,13 @@ import doobie.util.Read import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStatus +import za.co.absa.fadb.status.handling.implementations.StandardQueryStatusHandling class DoobieSingleResultFunctionWithStatusTest extends AnyFunSuite with DoobieTest { class CreateActor(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) - extends DoobieSingleResultFunctionWithStatus[CreateActorRequestBody, Int, IO] { + extends DoobieSingleResultFunctionWithStatus[CreateActorRequestBody, Int, IO] + with StandardQueryStatusHandling { override def sql(values: CreateActorRequestBody)(implicit read: Read[StatusWithData[Int]]): Fragment = { sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" @@ -41,5 +43,6 @@ class DoobieSingleResultFunctionWithStatusTest extends AnyFunSuite with DoobieTe val requestBody = CreateActorRequestBody("Pavel", "Marek") val result = createActor(requestBody).unsafeRunSync() assert(result.isRight) + println(result) } } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala index 778be934..d2b315e3 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala @@ -18,13 +18,12 @@ package za.co.absa.fadb.doobie import cats.Monad import cats.effect.Async -import cats.implicits.toFlatMapOps import cats.implicits._ import doobie._ import doobie.implicits._ import doobie.util.Read import za.co.absa.fadb.DBEngine -import za.co.absa.fadb.status.StatusException +import za.co.absa.fadb.exceptions.StatusException import scala.language.higherKinds @@ -54,7 +53,9 @@ class DoobieEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DB query.fragment.query[R].to[Seq].transact(transactor) } - private def executeQueryWithStatusHandling[R](query: QueryWithStatusType[R])(implicit readStatusWithDataR: Read[StatusWithData[R]]): F[Either[StatusException, R]] = { + private def executeQueryWithStatusHandling[R]( + query: QueryWithStatusType[R] + )(implicit readStatusWithDataR: Read[StatusWithData[R]]): F[Either[StatusException, R]] = { query.fragment.query[StatusWithData[R]].unique.transact(transactor).map(query.getResultOrException) } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index e658c25c..93544896 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -21,14 +21,15 @@ import cats.effect.kernel.Async import doobie.util.Read import doobie.util.fragment.Fragment import za.co.absa.fadb.DBFunction._ -import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema} +import za.co.absa.fadb.exceptions.StatusException +import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, FunctionStatusWithData} import scala.language.higherKinds trait DoobieFunctionBase[R] { /** - * The `Read[R]` instance used to read the query result into `R`. + * The `Read[R]` instance used to read the query result into `R`. */ implicit val readR: Read[R] } @@ -40,6 +41,7 @@ trait DoobieFunctionBase[R] { * @tparam R the result type of the function */ private[doobie] trait DoobieFunction[I, R] extends DoobieFunctionBase[R] { + /** * Generates a Doobie `Fragment` representing the SQL query for the function. * @@ -57,9 +59,10 @@ private[doobie] trait DoobieFunction[I, R] extends DoobieFunctionBase[R] { protected def query(values: I): DoobieQuery[R] = new DoobieQuery[R](sql(values)) } - private[doobie] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBase[R] { + def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] + /** * The `Read[StatusWithData[R]]` instance used to read the query result with status into `StatusWithData[R]`. */ @@ -81,7 +84,7 @@ private[doobie] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBase[ * @param values the input values for the function * @return the `DoobieQueryWithStatus[R]` representing the SQL query */ - protected def query(values: I): DoobieQueryWithStatus[R] = new DoobieQueryWithStatus[R](sql(values)) + protected def query(values: I): DoobieQueryWithStatus[R] = new DoobieQueryWithStatus[R](sql(values), checkStatus) } /** @@ -103,8 +106,8 @@ object DoobieFunction { */ abstract class DoobieSingleResultFunctionWithStatus[I, R, F[_]: Async: Monad]( functionNameOverride: Option[String] = None - )( - implicit override val schema: DBSchema, + )(implicit + override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R], val readSelectWithStatus: Read[StatusWithData[R]] @@ -122,7 +125,8 @@ object DoobieFunction { * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieSingleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, + implicit + override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] ) extends DBSingleResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) @@ -139,7 +143,8 @@ object DoobieFunction { * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieMultipleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, + implicit + override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] ) extends DBMultipleResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) @@ -156,7 +161,8 @@ object DoobieFunction { * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieOptionalResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, + implicit + override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] ) extends DBOptionalResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala index d019aca8..4d75fd99 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala @@ -18,12 +18,10 @@ package za.co.absa.fadb.doobie import doobie.util.Read import doobie.util.fragment.Fragment -import za.co.absa.fadb.status.handling.StandardQueryStatusHandling -import za.co.absa.fadb.status.{FunctionStatus, StatusException} +import za.co.absa.fadb.exceptions.StatusException +import za.co.absa.fadb.status.FunctionStatus import za.co.absa.fadb.{FunctionStatusWithData, Query, QueryWithStatus} -import scala.language.higherKinds - /** * `DoobieQuery` is a class that extends `Query` with `R` as the result type. * It uses Doobie's `Fragment` to represent SQL queries. @@ -33,14 +31,14 @@ import scala.language.higherKinds */ class DoobieQuery[R: Read](val fragment: Fragment)(implicit val readR: Read[R]) extends Query[R] -// QueryStatusHandling has to be mixed-in for the checkStatus method implementation -class DoobieQueryWithStatus[R](val fragment: Fragment)(implicit val readStatusWithDataR: Read[StatusWithData[R]]) - extends QueryWithStatus[StatusWithData[R], R, R] with StandardQueryStatusHandling { -// extends QueryWithStatus[A, B, R] { +class DoobieQueryWithStatus[R]( + val fragment: Fragment, + checkStatus: FunctionStatusWithData[R] => Either[StatusException, R] +)(implicit val readStatusWithDataR: Read[StatusWithData[R]]) extends QueryWithStatus[StatusWithData[R], R, R] { - override def processStatus(initialResult: StatusWithData[R]): FunctionStatusWithData[R] = - FunctionStatusWithData(FunctionStatus(initialResult.status, initialResult.status_text), initialResult.data) + override def processStatus(initialResult: StatusWithData[R]): FunctionStatusWithData[R] = + FunctionStatusWithData(FunctionStatus(initialResult.status, initialResult.status_text), initialResult.data) - override def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[R]): Either[StatusException, R] = - checkStatus(statusWithData) + override def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[R]): Either[StatusException, R] = + checkStatus(statusWithData) } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/Results.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/Results.scala deleted file mode 100644 index 734d7213..00000000 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/Results.scala +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb.doobie - -import za.co.absa.fadb.status.FunctionStatus - -object Results { - - /** - * `SuccessfulResult` is a case class that represents a successful result of a function. - * - * @param functionStatus the status of the function - * @param result the result of the function - */ - case class SuccessfulResult[R](functionStatus: FunctionStatus, result: R) - - /** - * `FailedResult` is a case class that represents a failed result of a function. - * - * @param functionStatus the status of the function - * @param failure the exception that caused the function to fail - */ - case class FailedResult(functionStatus: FunctionStatus, failure: Throwable) - - /** - * `ResultWithStatus` is a type alias for `Either[FailedResult, SuccessfulResult[R]]`. - * It represents a result of a function that can either be successful or failed. - */ - type ResultWithStatus[R] = Either[FailedResult, SuccessfulResult[R]] -} diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala new file mode 100644 index 00000000..3f37d33c --- /dev/null +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala @@ -0,0 +1,3 @@ +package za.co.absa.fadb.doobie + +case class StatusWithData[R](status: Int, status_text: String, data: R) diff --git a/examples/src/test/scala/za/co/absa/fadb/examples/enceladus/DatasetSchemaSuite.scala b/examples/src/test/scala/za/co/absa/fadb/examples/enceladus/DatasetSchemaSuite.scala index f305d748..3acb5958 100644 --- a/examples/src/test/scala/za/co/absa/fadb/examples/enceladus/DatasetSchemaSuite.scala +++ b/examples/src/test/scala/za/co/absa/fadb/examples/enceladus/DatasetSchemaSuite.scala @@ -20,8 +20,8 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec import za.co.absa.fadb.examples.enceladus.DatasetSchema._ import slick.jdbc.PostgresProfile.api._ +import za.co.absa.fadb.exceptions.StatusException import za.co.absa.fadb.slick.SlickPgEngine -import za.co.absa.fadb.status.StatusException import scala.concurrent.Await import scala.concurrent.duration.Duration diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala b/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala index 93ec6dcf..c7bb3942 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala @@ -29,34 +29,34 @@ import java.time.{Duration, LocalDate, LocalDateTime, LocalTime, OffsetDateTime, import java.util.UUID import scala.concurrent.Future -class FaDbPostgresProfileSuite extends AsyncFlatSpec { +class FaDbPostgresProfileSuite extends AsyncFlatSpec { private val database = Database.forConfig("postgrestestdb") private val testDBEngine: SlickPgEngine = new SlickPgEngine(database) behavior of "FaDbPostgresProfile" - it should "be able to pass through and extract extended Postgres types" in { - - case class InputOutput( - uuid1: UUID, //uuid - dateTime1: LocalDate, //date - dateTime2: LocalTime, //time - dateTime3: LocalDateTime, //timestamp - dateTime4: Duration, //interval - dateTime5: ZonedDateTime, //timestamptz - dateTime6: OffsetDateTime, //timestamptz - range1: Range[Int], //range - lTree1: LTree, //ltree - map1: Map[String, String],//hstore - inet1: InetString, //inet - macaddr1: MacAddrString //macaddr - ) - - class TestFunction(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) + it should "be able to pass through and extract extended Postgres types" in { + + case class InputOutput( + uuid1: UUID, // uuid + dateTime1: LocalDate, // date + dateTime2: LocalTime, // time + dateTime3: LocalDateTime, // timestamp + dateTime4: Duration, // interval + dateTime5: ZonedDateTime, // timestamptz + dateTime6: OffsetDateTime, // timestamptz + range1: Range[Int], // range + lTree1: LTree, // ltree + map1: Map[String, String], // hstore + inet1: InetString, // inet + macaddr1: MacAddrString // macaddr + ) + + class TestFunction(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) extends SlickSingleResultFunction[InputOutput, InputOutput] { - override protected def sql(values: InputOutput): SQLActionBuilder = { - sql"""SELECT #$selectEntry + override protected def sql(values: InputOutput): SQLActionBuilder = { + sql"""SELECT #$selectEntry FROM #$functionName( ${values.uuid1}, ${values.dateTime1}, @@ -71,9 +71,10 @@ class FaDbPostgresProfileSuite extends AsyncFlatSpec { ${values.inet1}, ${values.macaddr1} ) #$alias;""" - } + } - override protected def slickConverter: GetResult[InputOutput] = GetResult{r => InputOutput( + override protected def slickConverter: GetResult[InputOutput] = GetResult { r => + InputOutput( r.<<, r.<<, r.<<, @@ -86,59 +87,58 @@ class FaDbPostgresProfileSuite extends AsyncFlatSpec { r.<<, r.<<, r.<< - )} - } - - class TestSchema (implicit dBEngine: SlickPgEngine) extends DBSchema("public"){ - - val testFunction = new TestFunction + ) } + } + class TestSchema(implicit dBEngine: SlickPgEngine) extends DBSchema("public") { - val input = InputOutput( - UUID.randomUUID(), - LocalDate.now(), - LocalTime.now(), - LocalDateTime.now(), - Duration.ofMinutes(42), - ZonedDateTime.now(), - OffsetDateTime.now(), - range1 = Range(7, 13), - LTree(List("This", "is", "an", "LTree")), - Map("a" -> "Hello", "bb" -> "beautiful", "ccc" -> "world"), - InetString("168.0.0.1"), - MacAddrString("12:34:56:78:90:ab") - ) - // because postgres does not fully support time zone as Java, so we need to clear it for later successful assert - val expected = input.copy(dateTime5 = input.dateTime5.toOffsetDateTime.toZonedDateTime) - - - new TestSchema()(testDBEngine).testFunction(input).map(result => assert(result == expected)) - + val testFunction = new TestFunction } - it should "be able to pass through and extract extended Postgres types as Options" in { - - case class InputOutput( - uuid1: Option[UUID], //uuid - dateTime1: Option[LocalDate], //date - dateTime2: Option[LocalTime], //time - dateTime3: Option[LocalDateTime], //timestamp - dateTime4: Option[Duration], //interval - dateTime5: Option[ZonedDateTime], //timestamptz - dateTime6: Option[OffsetDateTime], //timestamptz - range1: Option[Range[Int]], //range - lTree1: Option[LTree], //ltree - map1: Option[Map[String, String]], //hstore - inet1: Option[InetString], //inet - macaddr1: Option[MacAddrString] //macaddr - ) - - class TestFunction(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) + val input = InputOutput( + UUID.randomUUID(), + LocalDate.now(), + LocalTime.now(), + LocalDateTime.now(), + Duration.ofMinutes(42), + ZonedDateTime.now(), + OffsetDateTime.now(), + range1 = Range(7, 13), + LTree(List("This", "is", "an", "LTree")), + Map("a" -> "Hello", "bb" -> "beautiful", "ccc" -> "world"), + InetString("168.0.0.1"), + MacAddrString("12:34:56:78:90:ab") + ) + // because postgres does not fully support time zone as Java, so we need to clear it for later successful assert + val expected = input.copy(dateTime5 = input.dateTime5.toOffsetDateTime.toZonedDateTime) + + new TestSchema()(testDBEngine).testFunction(input).map(result => assert(result == expected)) + + } + + it should "be able to pass through and extract extended Postgres types as Options" in { + + case class InputOutput( + uuid1: Option[UUID], // uuid + dateTime1: Option[LocalDate], // date + dateTime2: Option[LocalTime], // time + dateTime3: Option[LocalDateTime], // timestamp + dateTime4: Option[Duration], // interval + dateTime5: Option[ZonedDateTime], // timestamptz + dateTime6: Option[OffsetDateTime], // timestamptz + range1: Option[Range[Int]], // range + lTree1: Option[LTree], // ltree + map1: Option[Map[String, String]], // hstore + inet1: Option[InetString], // inet + macaddr1: Option[MacAddrString] // macaddr + ) + + class TestFunction(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) extends SlickSingleResultFunction[InputOutput, InputOutput] { - override protected def sql(values: InputOutput): SQLActionBuilder = { - sql"""SELECT #$selectEntry + override protected def sql(values: InputOutput): SQLActionBuilder = { + sql"""SELECT #$selectEntry FROM #$functionName( ${values.uuid1}, ${values.dateTime1}, @@ -153,9 +153,10 @@ class FaDbPostgresProfileSuite extends AsyncFlatSpec { ${values.inet1}, ${values.macaddr1} ) #$alias;""" - } + } - override protected def slickConverter: GetResult[InputOutput] = GetResult{r => InputOutput( + override protected def slickConverter: GetResult[InputOutput] = GetResult { r => + InputOutput( r.<<, r.<<, r.<<, @@ -168,31 +169,31 @@ class FaDbPostgresProfileSuite extends AsyncFlatSpec { r.nextHStoreOption, r.<<, r.nextMacAddrOption - )} + ) } + } - class TestSchema (implicit dBEngine: SlickPgEngine) extends DBSchema("public"){ + class TestSchema(implicit dBEngine: SlickPgEngine) extends DBSchema("public") { - val testFunction = new TestFunction - } - - val inputOutput = InputOutput( - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None - ) - - new TestSchema()(testDBEngine).testFunction(inputOutput).map(result => assert(result == inputOutput)) + val testFunction = new TestFunction } -} + val inputOutput = InputOutput( + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None + ) + + new TestSchema()(testDBEngine).testFunction(inputOutput).map(result => assert(result == inputOutput)) + } +} diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala index 901834b2..ae7b0893 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala @@ -30,7 +30,8 @@ import scala.concurrent.{Await, Future} class SlickMultipleResultFunctionTest extends AnyFunSuite with SlickTest { class GetActors(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) - extends SlickMultipleResultFunction[GetActorsQueryParameters, Actor] with ActorSlickConverter { + extends SlickMultipleResultFunction[GetActorsQueryParameters, Actor] + with ActorSlickConverter { override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("actor_id", "first_name", "last_name") diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala index 90487157..835cd8d0 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala @@ -29,7 +29,8 @@ import scala.concurrent.duration.DurationInt class SlickOptionalResultFunctionTest extends AnyFunSuite with SlickTest { class GetActorById(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) - extends SlickOptionalResultFunction[Int, Actor] with ActorSlickConverter { + extends SlickOptionalResultFunction[Int, Actor] + with ActorSlickConverter { override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("actor_id", "first_name", "last_name") diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala index f7110dc7..08a8494b 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala @@ -5,6 +5,7 @@ import slick.jdbc.{GetResult, SQLActionBuilder} import za.co.absa.fadb.DBSchema import za.co.absa.fadb.slick.SlickFunction.SlickSingleResultFunctionWithStatus import za.co.absa.fadb.slick.FaDbPostgresProfile.api._ +import za.co.absa.fadb.status.handling.implementations.StandardQueryStatusHandling import scala.concurrent.Await import scala.concurrent.ExecutionContext.Implicits.global @@ -12,16 +13,14 @@ import scala.concurrent.duration.DurationInt class SlickSingleResultFunctionWithStatusTest extends AnyFunSuite with SlickTest { class CreateActor(implicit schema: DBSchema, dbEngine: SlickPgEngine) - extends SlickSingleResultFunctionWithStatus[CreateActorRequestBody, Int] { + extends SlickSingleResultFunctionWithStatus[CreateActorRequestBody, Int] + with StandardQueryStatusHandling { override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("o_actor_id") override protected def sql(values: CreateActorRequestBody): SQLActionBuilder = sql"""SELECT #$selectEntry FROM #$functionName(${values.firstName},${values.lastName}) #$alias;""" - /** - * The `GetResult[R]` instance used to read the query result into `R`. - */ override protected def slickConverter: GetResult[Int] = GetResult(r => r.<<) } diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/FaDbPostgresProfile.scala b/slick/src/main/scala/za/co/absa/fadb/slick/FaDbPostgresProfile.scala index 9ebc57ac..f4fe92be 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/FaDbPostgresProfile.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/FaDbPostgresProfile.scala @@ -20,39 +20,38 @@ import com.github.tminglei.slickpg._ import za.co.absa.fadb.slick.support.PgUUIDSupport /** - * DB profile recommended to use with SlickPgEngine to offer support for all extended Postgres types. - * JSON is not included, as they are multiple JSON implementations. Choose the one of your liking and extend - * `FaDbPostgresProfile` with it. More on [SlickPG](https://github.com/tminglei/slick-pg/tree/master) page. - */ -trait FaDbPostgresProfile extends ExPostgresProfile - with PgArraySupport - with PgDate2Support - with PgNetSupport - with PgRangeSupport - with PgLTreeSupport - with PgHStoreSupport - with PgSearchSupport - with PgUUIDSupport - { - - trait API extends super.API + * DB profile recommended to use with SlickPgEngine to offer support for all extended Postgres types. + * JSON is not included, as they are multiple JSON implementations. Choose the one of your liking and extend + * `FaDbPostgresProfile` with it. More on [SlickPG](https://github.com/tminglei/slick-pg/tree/master) page. + */ +trait FaDbPostgresProfile + extends ExPostgresProfile + with PgArraySupport + with PgDate2Support + with PgNetSupport + with PgRangeSupport + with PgLTreeSupport + with PgHStoreSupport + with PgSearchSupport + with PgUUIDSupport { + + trait API + extends super.API with ArrayImplicits with Date2DateTimeImplicitsDuration with NetImplicits with RangeImplicits with LTreeImplicits with HStoreImplicits - with SimpleArrayPlainImplicits with Date2DateTimePlainImplicits with SimpleNetPlainImplicits with SimpleRangePlainImplicits with SimpleLTreePlainImplicits with SimpleHStorePlainImplicits - with UUIDPlainImplicits - override val api: API = new API {} - } + override val api: API = new API {} +} object FaDbPostgresProfile extends FaDbPostgresProfile diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala index 6a762290..15ac5540 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala @@ -19,7 +19,8 @@ package za.co.absa.fadb.slick import cats.implicits._ import slick.jdbc.{GetResult, SQLActionBuilder} import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBOptionalResultFunction, DBSingleResultFunction} -import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema} +import za.co.absa.fadb.exceptions.StatusException +import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, FunctionStatusWithData} import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -42,14 +43,14 @@ private[slick] trait SlickFunctionBase[I, R] { def fieldsToSelect: Seq[String] /** - * Alias to use within the SQL query + * Alias to use within the SQL query */ protected val alias = "FNC" /** - * Helper function to use in the actual DB function class + * Helper function to use in the actual DB function class * - * @return the SELECT part of the function call SQL query + * @return the SELECT part of the function call SQL query */ protected def selectEntry: String = { val fieldsSeq = fieldsToSelect @@ -79,37 +80,40 @@ private[slick] trait SlickFunction[I, R] extends SlickFunctionBase[I, R] { private[slick] trait SlickFunctionWithStatus[I, R] extends SlickFunctionBase[I, R] { + def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] + /** * Generates a `SlickQueryWithStatus[R]` representing the SQL query for the function with status support. * * @param status - the status to check * @return - Success or failure the status means */ - protected def query(values: I): SlickQueryWithStatus[R] = new SlickQueryWithStatus[R](sql(values), slickConverter) + protected def query(values: I): SlickQueryWithStatus[R] = + new SlickQueryWithStatus[R](sql(values), slickConverter, checkStatus) } object SlickFunction { - abstract class SlickSingleResultFunctionWithStatus[I, R](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, + abstract class SlickSingleResultFunctionWithStatus[I, R](functionNameOverride: Option[String] = None)(implicit + override val schema: DBSchema, DBEngine: SlickPgEngine ) extends DBFunctionWithStatus[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunctionWithStatus[I, R] - abstract class SlickSingleResultFunction[I, R](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, + abstract class SlickSingleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit + override val schema: DBSchema, DBEngine: SlickPgEngine ) extends DBSingleResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunction[I, R] - abstract class SlickMultipleResultFunction[I, R](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, + abstract class SlickMultipleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit + override val schema: DBSchema, DBEngine: SlickPgEngine ) extends DBMultipleResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunction[I, R] - abstract class SlickOptionalResultFunction[I, R](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, + abstract class SlickOptionalResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit + override val schema: DBSchema, DBEngine: SlickPgEngine ) extends DBOptionalResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunction[I, R] diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunctionWithStatusSupport.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunctionWithStatusSupport.scala deleted file mode 100644 index 86e43553..00000000 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunctionWithStatusSupport.scala +++ /dev/null @@ -1,66 +0,0 @@ -///* -// * Copyright 2022 ABSA Group Limited -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -// -//package za.co.absa.fadb.slick -// -//import slick.jdbc.{GetResult, PositionedResult} -//import za.co.absa.fadb.status.FunctionStatus -// -//import scala.util.Try -// -///** -// * An extension of the [[SlickFunction]] mix-in trait to add support of status handling -// * This trait expects another mix-in of [[za.co.absa.fadb.status.handling.StatusHandling StatusHandling]] (or implementation of `checkStatus` function) -// * -// * @tparam I - The input type of the function -// * @tparam R - The return type of the function -// */ -//trait SlickFunctionWithStatusSupport[I, R] extends SlickFunction[I, R] { -// -// /** -// * Function which should actually check the status code returned by the DB function. Expected to got implemented by -// * [[za.co.absa.fadb.status.handling.StatusHandling StatusHandling]] successor trait. But of course can be implemented directly. -// * -// * @param status - the status to check -// * @return - Success or failure the status means -// */ -// protected def checkStatus(status: FunctionStatus): Try[FunctionStatus] -// -// /** -// * A special extension of the converting function that first picks up status code and status check and checks for their -// * meaning. Then the original conversion is executed. -// * @param queryResult - the result of the SQL query, the input of the original converting function -// * @param actualConverter - the original converting function -// * @return - new converting function that also checks for status -// */ -// private def converterWithStatus(queryResult: PositionedResult, actualConverter: GetResult[R]): R = { -// val status:Int = queryResult.<< -// val statusText: String = queryResult.<< -// checkStatus(FunctionStatus(status, statusText)).get //throw exception if status was off -// actualConverter(queryResult) -// } -// -// /** -// * Replaces the converter with one that also extracts and checks status code and status text. -// * @param values - the values to pass over to the database function -// * @return - the SQL query in [[SlickQuery]] form -// */ -// override protected def query(values: I): dbEngine.QueryType[R] = { -// val original = super.query(values) -// new SlickQuery[R](original.sql, GetResult{converterWithStatus(_, original.getResult)}) -// } -// -//} diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala index 61becc9f..985952f1 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala @@ -16,47 +16,42 @@ package za.co.absa.fadb.slick - import za.co.absa.fadb.DBEngine import scala.concurrent.{ExecutionContext, Future} import slick.jdbc.PostgresProfile.api._ import cats.implicits._ -import za.co.absa.fadb.status.StatusException import scala.language.higherKinds import slick.jdbc.{GetResult, PositionedResult} +import za.co.absa.fadb.exceptions.StatusException /** - * [[DBEngine]] based on the Slick library in the Postgres flavor - * @param db - the Slick database + * [[DBEngine]] based on the Slick library in the Postgres flavor + * + * @param db - the Slick database */ class SlickPgEngine(val db: Database)(implicit val executor: ExecutionContext) extends DBEngine[Future] { /** - * The type of Queries for Slick - * @tparam T - the return type of the query + * The type of Queries for Slick + * @tparam T - the return type of the query */ type QueryType[R] = SlickQuery[R] type QueryWithStatusType[R] = SlickQueryWithStatus[R] /** - * Execution using Slick - * @param query - the Slick query to execute - * @tparam R - return the of the query - * @return - sequence of the results of database query + * Execution using Slick + * @param query - the Slick query to execute + * @tparam R - return the of the query + * @return - sequence of the results of database query */ override protected def run[R](query: QueryType[R]): Future[Seq[R]] = { - // It can be expected that a GetResult will be passed into the run function as converter. - // Unfortunately it has to be recreated to be used by Slick val slickAction = query.sql.as[R](query.getResult) db.run(slickAction) } override def fetchHeadWithStatus[R](query: QueryWithStatusType[R]): Future[Either[StatusException, R]] = { -// implicit val getPositionedResult: GetResult[PositionedResult] = GetResult(r => r) -// val slickAction = query.sql.as[PositionedResult].head.map(query.getResultOrException) -// val slickAction = query.sql.as[PositionedResult].head.map(query.test(query.getResult)) val slickAction = query.sql.as[Either[StatusException, R]](query.getStatusExceptionOrData).head db.run(slickAction) } diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala index 6e126272..a7e47df4 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala @@ -17,22 +17,25 @@ package za.co.absa.fadb.slick import slick.jdbc.{GetResult, PositionedResult, SQLActionBuilder} -import za.co.absa.fadb.status.handling.StandardQueryStatusHandling -import za.co.absa.fadb.status.{FunctionStatus, StatusException} +import za.co.absa.fadb.exceptions.StatusException +import za.co.absa.fadb.status.FunctionStatus import za.co.absa.fadb.{FunctionStatusWithData, Query, QueryWithStatus} /** - * SQL query representation for Slick - * @param sql - the SQL query in Slick format - * @param getResult - the converting function, that converts the [[slick.jdbc.PositionedResult slick.PositionedResult]] (the result of Slick + * SQL query representation for Slick + * @param sql - the SQL query in Slick format + * @param getResult - the converting function, that converts the [[slick.jdbc.PositionedResult slick.PositionedResult]] (the result of Slick * execution) into the desire `R` type - * @tparam R - the return type of the query + * @tparam R - the return type of the query */ class SlickQuery[R](val sql: SQLActionBuilder, val getResult: GetResult[R]) extends Query[R] -// QueryStatusHandling has to be mixed-in for the checkStatus method implementation -class SlickQueryWithStatus[R](val sql: SQLActionBuilder, val getResult: GetResult[R]) - extends QueryWithStatus[PositionedResult, PositionedResult, R] with StandardQueryStatusHandling { + +class SlickQueryWithStatus[R]( + val sql: SQLActionBuilder, + val getResult: GetResult[R], + checkStatus: FunctionStatusWithData[PositionedResult] => Either[StatusException, PositionedResult] +) extends QueryWithStatus[PositionedResult, PositionedResult, R] { override def processStatus(initialResult: PositionedResult): FunctionStatusWithData[PositionedResult] = { val status: Int = initialResult.<< @@ -40,10 +43,12 @@ class SlickQueryWithStatus[R](val sql: SQLActionBuilder, val getResult: GetResul FunctionStatusWithData(FunctionStatus(status, statusText), initialResult) } - override def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[PositionedResult]): Either[StatusException, R] = { + override def toStatusExceptionOrData( + statusWithData: FunctionStatusWithData[PositionedResult] + ): Either[StatusException, R] = { checkStatus(statusWithData) match { case Left(statusException) => Left(statusException) - case Right(value) => Right(getResult(value)) + case Right(value) => Right(getResult(value)) } } diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/support/PgUUIDSupport.scala b/slick/src/main/scala/za/co/absa/fadb/slick/support/PgUUIDSupport.scala index 1bab5ff5..695772fc 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/support/PgUUIDSupport.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/support/PgUUIDSupport.scala @@ -31,11 +31,11 @@ trait PgUUIDSupport extends PgCommonJdbcTypes { driver: PostgresProfile => // register types to let `ExModelBuilder` find them driver match { case profile: ExPostgresProfile => profile.bindPgTypeToScala("uuid", classTag[UUID]) - case _ => + case _ => } } - trait UUIDPlainImplicits extends UUIDCodeGenSupport{ + trait UUIDPlainImplicits extends UUIDCodeGenSupport { import utils.PlainSQLUtils._ implicit class PgPositionedResult(val r: PositionedResult) { @@ -44,12 +44,16 @@ trait PgUUIDSupport extends PgCommonJdbcTypes { driver: PostgresProfile => def nextUUIDOption: Option[UUID] = r.nextObjectOption().map(_.asInstanceOf[UUID]) } - implicit object SetUUID extends SetParameter[UUID] { def apply(v: UUID, pp: PositionedParameters): Unit = { pp.setObject(v, JDBCType.BINARY.getVendorTypeNumber) } } + implicit object SetUUID extends SetParameter[UUID] { + def apply(v: UUID, pp: PositionedParameters): Unit = { pp.setObject(v, JDBCType.BINARY.getVendorTypeNumber) } + } implicit val getUUID: GetResult[UUID] = mkGetResult(_.nextUUID) implicit val getUUIDOption: GetResult[Option[UUID]] = mkGetResult(_.nextUUIDOption) - implicit val setUUID: SetParameter[UUID] = new SetParameter[UUID] { def apply(v: UUID, pp: PositionedParameters): Unit = { pp.setObject(v, JDBCType.BINARY.getVendorTypeNumber) } } - implicit val setUUIDOption: SetParameter[Option[UUID]] = new SetParameter[Option[UUID]] { + implicit val setUUID: SetParameter[UUID] = new SetParameter[UUID] { + def apply(v: UUID, pp: PositionedParameters): Unit = { pp.setObject(v, JDBCType.BINARY.getVendorTypeNumber) } + } + implicit val setUUIDOption: SetParameter[Option[UUID]] = new SetParameter[Option[UUID]] { def apply(v: Option[UUID], pp: PositionedParameters): Unit = { v.map( pp.setObject(_, JDBCType.BINARY.getVendorTypeNumber) From bd2a21f6e451d5a40e6c743ffa71ba4cf60373c7 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 8 Dec 2023 12:51:34 +0100 Subject: [PATCH 25/90] StandardQueryStatusHandlingTest --- .gitignore | 2 + .../StandardQueryStatusHandling.scala | 3 +- .../fadb/status/StatusExceptionSuite.scala | 90 ++++++++----------- .../fadb/status/StatusHandlingSuite.scala | 43 --------- .../StandardQueryStatusHandlingTest.scala | 83 +++++++++++++++++ 5 files changed, 124 insertions(+), 97 deletions(-) delete mode 100644 core/src/test/scala/za/co/absa/fadb/status/StatusHandlingSuite.scala create mode 100644 core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandlingTest.scala diff --git a/.gitignore b/.gitignore index 899bff7f..705db96e 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,5 @@ dist test-output build.log .bsp +/.bloop/ +/.metals/ diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala index 70f17a42..2474d35a 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala @@ -1,8 +1,7 @@ package za.co.absa.fadb.status.handling.implementations import za.co.absa.fadb.FunctionStatusWithData -import za.co.absa.fadb.exceptions.{DataConflictException, DataNotFoundException, ErrorInDataException, OtherStatusException, ServerMisconfigurationException, StatusException, StatusOutOfRangeException} -import za.co.absa.fadb.status._ +import za.co.absa.fadb.exceptions._ import za.co.absa.fadb.status.handling.QueryStatusHandling trait StandardQueryStatusHandling extends QueryStatusHandling { diff --git a/core/src/test/scala/za/co/absa/fadb/status/StatusExceptionSuite.scala b/core/src/test/scala/za/co/absa/fadb/status/StatusExceptionSuite.scala index 501a3ae3..73ec77d7 100644 --- a/core/src/test/scala/za/co/absa/fadb/status/StatusExceptionSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/status/StatusExceptionSuite.scala @@ -1,52 +1,38 @@ -///* -// * Copyright 2022ABSA Group Limited -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -// -//package za.co.absa.fadb.status -// -//import org.scalatest.funsuite.AnyFunSuite -//import za.co.absa.fadb.exceptions.StatusException._ -// -//class StatusExceptionSuite extends AnyFunSuite { -// test("Test equals - when they are the same") { -// val statusException = DataConflictException(FunctionStatus(10, "OK")) -// val otherStatusException = DataConflictException(10, "OK") -// -// assert(statusException == otherStatusException) -// } -// -// test("Test equals - when they are different") { -// val statusException = DataNotFoundException(10, "OK") -// val otherStatusException = DataNotFoundException(10, "Hello") -// val anotherStatusException = DataNotFoundException(11, "OK") -// -// assert(statusException != otherStatusException) -// assert(statusException != anotherStatusException) -// } -// -// test("Test equals - when values are same but classes differ") { -// val statusException = StatusException(10, "OK") -// val otherStatusException = ServerMisconfigurationException(10, "OK") -// -// assert(statusException != otherStatusException) -// } -// -// test("Test equals - when values are same but classes inheritance differ") { -// val statusException = StatusException(10, "OK") -// val otherException = new ClassNotFoundException() -// -// assert(statusException != otherException) -// } -//} +/* + * Copyright 2022ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.fadb.status + +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.exceptions._ + +class StatusExceptionSuite extends AnyFunSuite { + test("Test equals - when they are the same") { + val statusException = DataConflictException(FunctionStatus(10, "OK")) + val otherStatusException = DataConflictException(FunctionStatus(10, "OK")) + + assert(statusException == otherStatusException) + } + + test("Test equals - when they are different") { + val statusException = DataNotFoundException(FunctionStatus(10, "OK")) + val otherStatusException = DataNotFoundException(FunctionStatus(10, "Hello")) + val anotherStatusException = DataNotFoundException(FunctionStatus(11, "OK")) + + assert(statusException != otherStatusException) + assert(statusException != anotherStatusException) + } +} diff --git a/core/src/test/scala/za/co/absa/fadb/status/StatusHandlingSuite.scala b/core/src/test/scala/za/co/absa/fadb/status/StatusHandlingSuite.scala deleted file mode 100644 index f3b76f91..00000000 --- a/core/src/test/scala/za/co/absa/fadb/status/StatusHandlingSuite.scala +++ /dev/null @@ -1,43 +0,0 @@ -///* -// * Copyright 2022 ABSA Group Limited -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -// -//package za.co.absa.fadb.status -// -//import org.scalatest.funsuite.AnyFunSuite -//import za.co.absa.fadb.DBFunctionFabric -//import za.co.absa.fadb.naming.NamingConvention -//import za.co.absa.fadb.naming.implementations.SnakeCaseNaming -//import za.co.absa.fadb.status.handling.StatusHandling -// -//class StatusHandlingSuite extends AnyFunSuite { -// test("Fields to select filled with default values") { -// trait FooDBFunction extends DBFunctionFabric { -// override def fieldsToSelect: Seq[String] = Seq("alpha", "beta") -// } -// -// class StatusHandlingForTest extends FooDBFunction with StatusHandling { -// override def functionName: String = "Never needed" -// override def namingConvention: NamingConvention = SnakeCaseNaming.Implicits.namingConvention -// -// override protected def checkStatus(status: FunctionStatus) = throw new Exception("Should never get here") -// override def fieldsToSelect: Seq[String] = super.fieldsToSelect -// } -// -// val statusHandling = new StatusHandlingForTest -// assert(statusHandling.fieldsToSelect == Seq("status", "status_text", "alpha", "beta")) -// -// } -//} diff --git a/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandlingTest.scala b/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandlingTest.scala new file mode 100644 index 00000000..cc1c81b3 --- /dev/null +++ b/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandlingTest.scala @@ -0,0 +1,83 @@ +package za.co.absa.fadb.status.handling.implementations + +import org.scalatest.funsuite.AnyFunSuiteLike +import org.scalatest.matchers.should.Matchers.{a, convertToAnyShouldWrapper} +import za.co.absa.fadb.FunctionStatusWithData +import za.co.absa.fadb.exceptions._ +import za.co.absa.fadb.status.FunctionStatus + +class StandardQueryStatusHandlingTest extends AnyFunSuiteLike { + + private val standardQueryStatusHandling = new StandardQueryStatusHandling {} + + test("checkStatus should return Right when status code is in the range 10-19") { + for (statusCode <- 10 to 19) { + val functionStatus = FunctionStatus(statusCode, "Success") + val statusWithData = FunctionStatusWithData(functionStatus, "Data") + val result = standardQueryStatusHandling.checkStatus(statusWithData) + result shouldBe Right("Data") + } + } + + test("checkStatus should return Left with ServerMisconfigurationException when status code is in the range 20-29") { + for (statusCode <- 20 to 29) { + val functionStatus = FunctionStatus(statusCode, "Server Misconfiguration") + val statusWithData = FunctionStatusWithData(functionStatus, "Data") + val result = standardQueryStatusHandling.checkStatus(statusWithData) + result shouldBe Left(ServerMisconfigurationException(functionStatus)) + } + } + + test("checkStatus should return Left with DataConflictException when status code is in the range 30-39") { + for (statusCode <- 30 to 39) { + val functionStatus = FunctionStatus(statusCode, "Data Conflict") + val statusWithData = FunctionStatusWithData(functionStatus, "Data") + val result = standardQueryStatusHandling.checkStatus(statusWithData) + result shouldBe Left(DataConflictException(functionStatus)) + } + } + + test("checkStatus should return Left with DataNotFoundException when status code is in the range 40-49") { + for (statusCode <- 40 to 49) { + val functionStatus = FunctionStatus(statusCode, "Data Not Found") + val statusWithData = FunctionStatusWithData(functionStatus, "Data") + val result = standardQueryStatusHandling.checkStatus(statusWithData) + result shouldBe Left(DataNotFoundException(functionStatus)) + } + } + + test("checkStatus should return Left with ErrorInDataException when status code is in the range 50-89") { + for (statusCode <- 50 to 89) { + val functionStatus = FunctionStatus(statusCode, "Error in Data") + val statusWithData = FunctionStatusWithData(functionStatus, "Data") + val result = standardQueryStatusHandling.checkStatus(statusWithData) + result shouldBe Left(ErrorInDataException(functionStatus)) + } + } + + test("checkStatus should return Left with OtherStatusException when status code is in the range 90-99") { + for (statusCode <- 90 to 99) { + val functionStatus = FunctionStatus(statusCode, "Other Status") + val statusWithData = FunctionStatusWithData(functionStatus, "Data") + val result = standardQueryStatusHandling.checkStatus(statusWithData) + result shouldBe Left(OtherStatusException(functionStatus)) + } + } + + test("checkStatus should return Left with StatusOutOfRangeException when status code is not in any known range") { + for (statusCode <- 0 to 9) { + val functionStatus = FunctionStatus(statusCode, "Out of range") + val statusWithData = FunctionStatusWithData(functionStatus, "Data") + val result = standardQueryStatusHandling.checkStatus(statusWithData) + result shouldBe Left(StatusOutOfRangeException(functionStatus)) + } + + for (statusCode <- 100 to 110) { + val functionStatus = FunctionStatus(statusCode, "Out of range") + val statusWithData = FunctionStatusWithData(functionStatus, "Data") + val result = standardQueryStatusHandling.checkStatus(statusWithData) + result shouldBe Left(StatusOutOfRangeException(functionStatus)) + } + } + +} From 45e483e59c804e86a05d3ecccd945c2cd93531a4 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 8 Dec 2023 12:57:52 +0100 Subject: [PATCH 26/90] Slick tests refactoring --- .../fadb/slick/SlickMultipleResultFunctionTest.scala | 8 +++----- .../fadb/slick/SlickOptionalResultFunctionTest.scala | 7 +++---- .../SlickSingleResultFunctionWithStatusTest.scala | 12 +++++------- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala index ae7b0893..4dde7557 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala @@ -16,18 +16,16 @@ package za.co.absa.fadb.slick +import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import slick.jdbc.SQLActionBuilder -import za.co.absa.fadb.DBFunction.DBMultipleResultFunction import za.co.absa.fadb.DBSchema import za.co.absa.fadb.slick.FaDbPostgresProfile.api._ import za.co.absa.fadb.slick.SlickFunction.SlickMultipleResultFunction import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.duration.DurationInt -import scala.concurrent.{Await, Future} -class SlickMultipleResultFunctionTest extends AnyFunSuite with SlickTest { +class SlickMultipleResultFunctionTest extends AnyFunSuite with SlickTest with ScalaFutures { class GetActors(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) extends SlickMultipleResultFunction[GetActorsQueryParameters, Actor] @@ -45,6 +43,6 @@ class SlickMultipleResultFunctionTest extends AnyFunSuite with SlickTest { test("SlickTest") { val expectedResultElem = Actor(49, "Pavel", "Marek") val results = getActors(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))) - assert(Await.result(results, 5.seconds).contains(expectedResultElem)) + assert(results.futureValue.contains(expectedResultElem)) } } diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala index 835cd8d0..e4f761d0 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala @@ -16,17 +16,16 @@ package za.co.absa.fadb.slick +import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import slick.jdbc.SQLActionBuilder import za.co.absa.fadb.DBSchema import za.co.absa.fadb.slick.FaDbPostgresProfile.api._ import za.co.absa.fadb.slick.SlickFunction.SlickOptionalResultFunction -import scala.concurrent.Await import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.duration.DurationInt -class SlickOptionalResultFunctionTest extends AnyFunSuite with SlickTest { +class SlickOptionalResultFunctionTest extends AnyFunSuite with SlickTest with ScalaFutures { class GetActorById(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) extends SlickOptionalResultFunction[Int, Actor] @@ -44,6 +43,6 @@ class SlickOptionalResultFunctionTest extends AnyFunSuite with SlickTest { test("SlickTest") { val expectedResultElem = Some(Actor(49, "Pavel", "Marek")) val results = getActorById(49) - assert(Await.result(results, 5.seconds) == expectedResultElem) + assert(results.futureValue == expectedResultElem) } } diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala index 08a8494b..e4ebacf8 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala @@ -1,17 +1,16 @@ package za.co.absa.fadb.slick +import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import slick.jdbc.{GetResult, SQLActionBuilder} import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.slick.SlickFunction.SlickSingleResultFunctionWithStatus import za.co.absa.fadb.slick.FaDbPostgresProfile.api._ +import za.co.absa.fadb.slick.SlickFunction.SlickSingleResultFunctionWithStatus import za.co.absa.fadb.status.handling.implementations.StandardQueryStatusHandling -import scala.concurrent.Await import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.duration.DurationInt -class SlickSingleResultFunctionWithStatusTest extends AnyFunSuite with SlickTest { +class SlickSingleResultFunctionWithStatusTest extends AnyFunSuite with SlickTest with ScalaFutures { class CreateActor(implicit schema: DBSchema, dbEngine: SlickPgEngine) extends SlickSingleResultFunctionWithStatus[CreateActorRequestBody, Int] with StandardQueryStatusHandling { @@ -28,8 +27,7 @@ class SlickSingleResultFunctionWithStatusTest extends AnyFunSuite with SlickTest test("SlickTest with status handling") { val requestBody = CreateActorRequestBody("Pavel", "Marek") - val result = createActor(requestBody) - assert(Await.result(result, 5.seconds).isRight) - println(result) + val result = createActor(requestBody).futureValue + assert(result.isRight) } } From 6684599b1c7cab87acbe9e680fb4096f1f95772d Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 8 Dec 2023 13:07:01 +0100 Subject: [PATCH 27/90] exceptions with docs --- .../fadb/exceptions/NamingException.scala | 4 +++ .../fadb/exceptions/StatusException.scala | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/core/src/main/scala/za/co/absa/fadb/exceptions/NamingException.scala b/core/src/main/scala/za/co/absa/fadb/exceptions/NamingException.scala index 1d59ac83..97d6ef34 100644 --- a/core/src/main/scala/za/co/absa/fadb/exceptions/NamingException.scala +++ b/core/src/main/scala/za/co/absa/fadb/exceptions/NamingException.scala @@ -16,4 +16,8 @@ package za.co.absa.fadb.exceptions +/** + * Exception thrown when a naming convention is not found for a given string + * @param message - the message to display + */ case class NamingException(message: String) extends Exception(message) diff --git a/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala b/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala index d5272d7e..560747fe 100644 --- a/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala +++ b/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala @@ -2,11 +2,37 @@ package za.co.absa.fadb.exceptions import za.co.absa.fadb.status.FunctionStatus +/** + * Represents an exception that is returned when the function status is not successful. + */ sealed abstract class StatusException(val status: FunctionStatus) extends Exception(status.statusText) +/** + * Represents an exception that is returned when there is a server misconfiguration. + */ final case class ServerMisconfigurationException(override val status: FunctionStatus) extends StatusException(status) + +/** + * Represents an exception that is returned when there is a data conflict. + */ final case class DataConflictException(override val status: FunctionStatus) extends StatusException(status) + +/** + * Represents an exception that is returned when data is not found. + */ final case class DataNotFoundException(override val status: FunctionStatus) extends StatusException(status) + +/** + * Represents an exception that is returned when there is an error in data. + */ final case class ErrorInDataException(override val status: FunctionStatus) extends StatusException(status) + +/** + * Represents an exception that is returned for other statuses. + */ final case class OtherStatusException(override val status: FunctionStatus) extends StatusException(status) + +/** + * Represents an exception that is returned when the status is out of range. + */ final case class StatusOutOfRangeException(override val status: FunctionStatus) extends StatusException(status) From 320073e0bafe45aba94f28055bac4f46b059f89b Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 8 Dec 2023 13:27:01 +0100 Subject: [PATCH 28/90] docs for the supporting classes --- .../fadb/naming/ExplicitNamingRequired.scala | 17 ++++++++++++++++ .../za/co/absa/fadb/naming/LettersCase.scala | 20 +++++++++++++++++++ .../absa/fadb/naming/NamingConvention.scala | 20 +++++++++++++++++++ .../naming/implementations/AsIsNaming.scala | 17 ++++++++++++++++ .../implementations/SnakeCaseNaming.scala | 17 ++++++++++++++++ .../status/handling/QueryStatusHandling.scala | 9 +++++++++ .../StandardQueryStatusHandling.scala | 11 ++++++++++ 7 files changed, 111 insertions(+) diff --git a/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala b/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala index 1f65c49e..98d1b5f9 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala @@ -18,15 +18,32 @@ package za.co.absa.fadb.naming import za.co.absa.fadb.exceptions.NamingException + +/** + * `ExplicitNamingRequired` is a [[NamingConvention]] that throws a [[NamingException]] for any string. + * This is used when explicit naming is required and no other naming convention should be applied. + */ class ExplicitNamingRequired extends NamingConvention { + + /** + * Throws a [[NamingException]] with a message indicating that explicit naming is required. + * @param original - The original string. + * @return Nothing, as a [[NamingException]] is always thrown. + */ override def stringPerConvention(original: String): String = { val message = s"No convention for '$original', explicit naming required." throw NamingException(message) } } +/** + * `ExplicitNamingRequired.Implicits` provides an implicit [[NamingConvention]] instance that throws a [[NamingException]] for any string. + */ object ExplicitNamingRequired { object Implicits { + /** + * An implicit [[NamingConvention]] instance that throws a [[NamingException]] for any string. + */ implicit val namingConvention: NamingConvention = new ExplicitNamingRequired() } } diff --git a/core/src/main/scala/za/co/absa/fadb/naming/LettersCase.scala b/core/src/main/scala/za/co/absa/fadb/naming/LettersCase.scala index 9fad8113..f5554d9b 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/LettersCase.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/LettersCase.scala @@ -16,17 +16,37 @@ package za.co.absa.fadb.naming +/** + * `LettersCase` is a sealed trait that represents different cases of letters. + * It provides a method to convert a string to the specific case. + */ sealed trait LettersCase { + /** + * Converts a string to the specific case. + * @param s - The original string. + * @return The string converted to the specific case. + */ def convert(s: String): String } object LettersCase { + /** + * `AsIs` is a [[LettersCase]] that leaves strings as they are. + */ case object AsIs extends LettersCase { override def convert(s: String): String = s } + + /** + * `LowerCase` is a [[LettersCase]] that converts strings to lower case. + */ case object LowerCase extends LettersCase { override def convert(s: String): String = s.toLowerCase } + + /** + * `UpperCase` is a [[LettersCase]] that converts strings to upper case. + */ case object UpperCase extends LettersCase { override def convert(s: String): String = s.toUpperCase } diff --git a/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala b/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala index 5203585c..cfb27213 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala @@ -16,7 +16,17 @@ package za.co.absa.fadb.naming +/** + * `NamingConvention` is a base trait that defines the interface for different naming conventions. + * It provides methods to convert a class name according to given naming convention. + */ trait NamingConvention { + + /** + * Converts the class name according to the specific naming convention. + * @param c - The class. + * @return The class name converted to string according to the specific naming convention. + */ def fromClassNamePerConvention(c: Class[_]): String = { val className = c.getSimpleName val cleanClassName = className.lastIndexOf('$') match { @@ -26,9 +36,19 @@ trait NamingConvention { stringPerConvention(cleanClassName) } + /** + * Converts the class name according to the specific naming convention. + * @param instance - The instance of the class. + * @return The class name converted to string according to the specific naming convention. + */ def fromClassNamePerConvention(instance: AnyRef): String = { fromClassNamePerConvention(instance.getClass) } + /** + * Converts the original string according to the specific naming convention. + * @param original - The original string. + * @return The original string converted according to the specific naming convention. + */ def stringPerConvention(original: String): String } diff --git a/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala b/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala index 6908174b..6e31e925 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala @@ -19,14 +19,31 @@ package za.co.absa.fadb.naming.implementations import za.co.absa.fadb.naming.{LettersCase, NamingConvention} import LettersCase.AsIs +/** + * `AsIsNaming` provides a naming convention that leaves strings as they are. + * It implements the [[NamingConvention]] trait. + * @param lettersCase - The case of the letters in the string. + */ class AsIsNaming(lettersCase: LettersCase) extends NamingConvention { + + /** + * Returns the original string converted to the specified letter case. + * @param original - The original string. + * @return The original string converted to the specified letter case. + */ override def stringPerConvention(original: String): String = { lettersCase.convert(original) } } +/** + * `AsIsNaming.Implicits` provides an implicit [[NamingConvention]] instance that leaves strings as they are. + */ object AsIsNaming { object Implicits { + /** + * An implicit [[NamingConvention]] instance that leaves strings as they are. + */ implicit val namingConvention: NamingConvention = new AsIsNaming(AsIs) } } diff --git a/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala b/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala index db2bdba7..394d3cc4 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala @@ -19,7 +19,13 @@ package za.co.absa.fadb.naming.implementations import za.co.absa.fadb.naming.{LettersCase, NamingConvention} import LettersCase.LowerCase +/** + * `SnakeCaseNaming` provides a naming convention that converts camel case strings to snake case. + * It implements the [[NamingConvention]] trait. + * @param lettersCase - The case of the letters in the string. + */ class SnakeCaseNaming(lettersCase: LettersCase) extends NamingConvention { + private def camelCaseToSnakeCase(s: String): String = { s.replaceAll("([A-Z])", "_$1") } @@ -34,13 +40,24 @@ class SnakeCaseNaming(lettersCase: LettersCase) extends NamingConvention { } } + /** + * Converts the original string to snake case and the specified letter case. + * @param original - The original string. + * @return The original string converted to snake case and the specified letter case. + */ override def stringPerConvention(original: String): String = { lettersCase.convert(stripIfFirstChar(camelCaseToSnakeCase(original), '_')) } } +/** + * `SnakeCaseNaming.Implicits` provides an implicit [[NamingConvention]] instance that converts camel case strings to snake case. + */ object SnakeCaseNaming { object Implicits { + /** + * An implicit [[NamingConvention]] instance that converts camel case strings to snake case. + */ implicit val namingConvention: NamingConvention = new SnakeCaseNaming(LowerCase) } } diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala index cd6c3bde..95bb34aa 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala @@ -3,6 +3,15 @@ package za.co.absa.fadb.status.handling import za.co.absa.fadb.FunctionStatusWithData import za.co.absa.fadb.exceptions.StatusException +/** + * `QueryStatusHandling` is a base trait that defines the interface for handling the status of a function invocation. + * It provides a method to check the status of a function invocation with data. + */ trait QueryStatusHandling { + /** + * Checks the status of a function invocation. + * @param statusWithData - The status of the function invocation with data. + * @return Either a [[StatusException]] if the status code indicates an error, or the data if the status code is successful. + */ def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] } diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala index 2474d35a..5ceb67c9 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala @@ -4,7 +4,18 @@ import za.co.absa.fadb.FunctionStatusWithData import za.co.absa.fadb.exceptions._ import za.co.absa.fadb.status.handling.QueryStatusHandling +/** + * `StandardQueryStatusHandling` is a trait that extends the [[QueryStatusHandling]] interface. + * It provides a standard implementation for checking the status of a function invocation. + */ trait StandardQueryStatusHandling extends QueryStatusHandling { + + /** + * Checks the status of a function invocation. + * @param statusWithData - The status of the function invocation with the data returned by the function. + * @tparam A - The type of the data returned by the function. + * @return Either the data returned by the function or an exception. + */ override def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] = { val functionStatus = statusWithData.functionStatus functionStatus.statusCode / 10 match { From 62f62b7f5e7e2e52e7e3e042d2a0c4956266b6fe Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 8 Dec 2023 13:52:22 +0100 Subject: [PATCH 29/90] docs for dbfunction and query --- .../main/scala/za/co/absa/fadb/DBEngine.scala | 8 +- .../scala/za/co/absa/fadb/DBFunction.scala | 138 +++++++++++------- .../co/absa/fadb/FunctionStatusWithData.scala | 6 + .../main/scala/za/co/absa/fadb/Query.scala | 22 ++- .../za/co/absa/fadb/doobie/DoobieEngine.scala | 6 +- .../za/co/absa/fadb/slick/SlickPgEngine.scala | 2 +- 6 files changed, 121 insertions(+), 61 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala index 0928d351..94348f5a 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala @@ -43,12 +43,12 @@ abstract class DBEngine[F[_]: Monad] { protected def run[R](query: QueryType[R]): F[Seq[R]] /** - * The actual query executioner of the queries of the engine + * The actual query executioner of the queries of the engine with status * @param query - the query to execute * @tparam R - return the of the query * @return - sequence of the results of database query */ - def fetchHeadWithStatus[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] + def runWithStatus[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] /** * Public method to execute when query is expected to return multiple results @@ -56,7 +56,9 @@ abstract class DBEngine[F[_]: Monad] { * @tparam R - return the of the query * @return - sequence of the results of database query */ - def fetchAll[R](query: QueryType[R]): F[Seq[R]] = run(query) + def fetchAll[R](query: QueryType[R]): F[Seq[R]] = { + run(query) + } /** * Public method to execute when query is expected to return exactly one row diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 80a2c395..67e62dd4 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -23,14 +23,14 @@ import za.co.absa.fadb.status.handling.QueryStatusHandling import scala.language.higherKinds /** - * @param functionNameOverride - in case the class name would not match the database function name, this gives the - * possibility of override - * @param schema - the schema the function belongs into - * @param dBEngine - the database engine that is supposed to execute the function (presumably contains - * connection to the database - * @tparam I - the type covering the input fields of the database function - * @tparam R - the type covering the returned fields from the database function - * @tparam E - the type of the [[DBEngine]] engine + * `DBFunction` is an abstract class that represents a database function. + * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. + * @param schema - The schema the function belongs to. + * @param dBEngine - The database engine that is supposed to execute the function (contains connection to the database). + * @tparam I - The type covering the input fields of the database function. + * @tparam R - The type covering the returned fields from the database function. + * @tparam E - The type of the [[DBEngine]] engine. + * @tparam F - The type of the context in which the database function is executed. */ abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, @@ -44,19 +44,47 @@ abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverr def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) /** - * Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the - * DBEngine specific mix-in. - * @param values - the values to pass over to the database function - * @return - the SQL query in the format specific to the provided [[DBEngine]] + * Function to create the DB function call specific to the provided [[DBEngine]]. + * Expected to be implemented by the DBEngine specific mix-in. + * @param values - The values to pass over to the database function. + * @return - The SQL query in the format specific to the provided [[DBEngine]]. */ protected def query(values: I): dBEngine.QueryType[R] - /*these 3 functions has to be defined here and not in the ancestors, as there the query type is not compatible - path-dependent types*/ + /** + * Executes the database function and returns multiple results. + * @param values - The values to pass over to the database function. + * @return - A sequence of results from the database function. + */ protected def multipleResults(values: I): F[Seq[R]] = dBEngine.fetchAll(query(values)) + + /** + * Executes the database function and returns a single result. + * @param values - The values to pass over to the database function. + * @return - A single result from the database function. + */ protected def singleResult(values: I): F[R] = dBEngine.fetchHead(query(values)) + + /** + * Executes the database function and returns an optional result. + * @param values - The values to pass over to the database function. + * @return - An optional result from the database function. + */ protected def optionalResult(values: I): F[Option[R]] = dBEngine.fetchHeadOption(query(values)) } +/** + * `DBFunctionWithStatus` is an abstract class that represents a database function with a status. + * It extends the [[DBFunction]] class and adds handling for the status of the function invocation. + * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. + * @param schema - The schema the function belongs to. + * @param dBEngine - The database engine that is supposed to execute the function (contains connection to the database). + * @param queryStatusHandling - The [[QueryStatusHandling]] instance that handles the status of the function invocation. + * @tparam I - The type covering the input fields of the database function. + * @tparam R - The type covering the returned fields from the database function. + * @tparam E - The type of the [[DBEngine]] engine. + * @tparam F - The type of the context in which the database function is executed. + */ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, @@ -73,20 +101,24 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functio /** * Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the * DBEngine specific mix-in. - * @param values - the values to pass over to the database function - * @return - the SQL query in the format specific to the provided [[DBEngine]] + * @param values the values to pass over to the database function + * @return the SQL query in the format specific to the provided [[DBEngine]] */ protected def query(values: I): dBEngine.QueryWithStatusType[R] - def apply(values: I): F[Either[StatusException, R]] = dBEngine.fetchHeadWithStatus(query(values)) + /** + * Executes the database function and returns multiple results. + * @param values + * @return A sequence of results from the database function. + */ + def apply(values: I): F[Either[StatusException, R]] = dBEngine.runWithStatus(query(values)) val defaultStatusField = "status" val defaultStatusTextField = "statusText" /** - * A mix-in to add the status fields into the SELECT statement - * - * @return a sequence of fields to use in SELECT + * The fields to select from the database function call + * @return the fields to select from the database function call */ override def fieldsToSelect: Seq[String] = { Seq( @@ -95,23 +127,23 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functio ) ++ super.fieldsToSelect } - // implementation to be mixed in + // To be provided by an implementation of QueryStatusHandling override def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] } object DBFunction { - /** - * Represents a function returning a set (in DB sense) of rows - * @param functionNameOverride - in case the class name would not match the database function name, this gives the - * possibility of override - * @param schema - the schema the function belongs into - * @param dBEngine - the database engine that is supposed to execute the function (presumably contains - * connection to the database - * @tparam I - the type covering the input fields of the database function - * @tparam R - the type covering the returned fields from the database function - * @tparam E - the type of the [[DBEngine]] engine - */ +/** + * `DBMultipleResultFunction` is an abstract class that represents a database function returning multiple results. + * It extends the [[DBFunction]] class and overrides the apply method to return a sequence of results. + * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. + * @param schema - The schema the function belongs to. + * @param dBEngine - The database engine that is supposed to execute the function (presumably contains connection to the database). + * @tparam I - The type covering the input fields of the database function. + * @tparam R - The type covering the returned fields from the database function. + * @tparam E - The type of the [[DBEngine]] engine. + * @tparam F - The type of the context in which the database function is executed. + */ abstract class DBMultipleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( functionNameOverride: Option[String] = None )(implicit schema: DBSchema, dBEngine: E) @@ -132,17 +164,17 @@ object DBFunction { def apply(values: I): F[Seq[R]] = multipleResults(values) } - /** - * Represents a function returning exactly one record - * @param functionNameOverride - in case the class name would not match the database function name, this gives the - * possibility of override - * @param schema - the schema the function belongs into - * @param dBEngine - the database engine that is supposed to execute the function (presumably contains - * connection to the database - * @tparam I - the type covering the input fields of the database function - * @tparam R - the type covering the returned fields from the database function - * @tparam E - the type of the [[DBEngine]] engine - */ +/** + * `DBSingleResultFunction` is an abstract class that represents a database function returning a single result. + * It extends the [[DBFunction]] class and overrides the apply method to return a single result. + * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. + * @param schema - The schema the function belongs to. + * @param dBEngine - The database engine that is supposed to execute the function (presumably contains connection to the database). + * @tparam I - The type covering the input fields of the database function. + * @tparam R - The type covering the returned fields from the database function. + * @tparam E - The type of the [[DBEngine]] engine. + * @tparam F - The type of the context in which the database function is executed. + */ abstract class DBSingleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( functionNameOverride: Option[String] = None )(implicit schema: DBSchema, dBEngine: E) @@ -162,17 +194,17 @@ object DBFunction { def apply(values: I): F[R] = singleResult(values) } - /** - * Represents a function returning one optional record - * @param functionNameOverride - in case the class name would not match the database function name, this gives the - * possibility of override - * @param schema - the schema the function belongs into - * @param dBEngine - the database engine that is supposed to execute the function (presumably contains - * connection to the database - * @tparam I - the type covering the input fields of the database function - * @tparam R - the type covering the returned fields from the database function - * @tparam E - the type of the [[DBEngine]] engine - */ +/** + * `DBOptionalResultFunction` is an abstract class that represents a database function returning an optional result. + * It extends the [[DBFunction]] class and overrides the apply method to return an optional result. + * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. + * @param schema - The schema the function belongs to. + * @param dBEngine - The database engine that is supposed to execute the function (presumably contains connection to the database). + * @tparam I - The type covering the input fields of the database function. + * @tparam R - The type covering the returned fields from the database function. + * @tparam E - The type of the [[DBEngine]] engine. + * @tparam F - The type of the context in which the database function is executed. + */ abstract class DBOptionalResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( functionNameOverride: Option[String] = None )(implicit schema: DBSchema, dBEngine: E) diff --git a/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala b/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala index db7f0533..f2000d62 100644 --- a/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala +++ b/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala @@ -2,4 +2,10 @@ package za.co.absa.fadb import za.co.absa.fadb.status.FunctionStatus +/** + * Represents a function status with data. + * @param functionStatus the function status + * @param data the data + * @tparam A the type of the data + */ case class FunctionStatusWithData[A](functionStatus: FunctionStatus, data: A) diff --git a/core/src/main/scala/za/co/absa/fadb/Query.scala b/core/src/main/scala/za/co/absa/fadb/Query.scala index 87dc1475..0bac83da 100644 --- a/core/src/main/scala/za/co/absa/fadb/Query.scala +++ b/core/src/main/scala/za/co/absa/fadb/Query.scala @@ -20,14 +20,34 @@ import za.co.absa.fadb.exceptions.StatusException /** * The basis for all query types of [[DBEngine]] implementations - * * @tparam R - the return type of the query */ trait Query[R] +/** + * The basis for all query types of [[DBEngine]] implementations with status + * @tparam R - the return type of the query + */ trait QueryWithStatus[A, B, R] { + /** + * Processes the status of the query and returns the status with data + * @param initialResult - the initial result of the query + * @return the status with data + */ def processStatus(initialResult: A): FunctionStatusWithData[B] + + /** + * Converts the status with data to either a status exception or the data + * @param statusWithData - the status with data + * @return either a status exception or the data + */ def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[B]): Either[StatusException, R] + + /** + * Returns the result of the query or a status exception + * @param initialResult - the initial result of the query + * @return the result of the query or a status exception + */ def getResultOrException(initialResult: A): Either[StatusException, R] = toStatusExceptionOrData( processStatus(initialResult) ) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala index d2b315e3..27612fca 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala @@ -53,7 +53,7 @@ class DoobieEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DB query.fragment.query[R].to[Seq].transact(transactor) } - private def executeQueryWithStatusHandling[R]( + private def executeQueryWithStatus[R]( query: QueryWithStatusType[R] )(implicit readStatusWithDataR: Read[StatusWithData[R]]): F[Either[StatusException, R]] = { query.fragment.query[StatusWithData[R]].unique.transact(transactor).map(query.getResultOrException) @@ -68,7 +68,7 @@ class DoobieEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DB override def run[R](query: QueryType[R]): F[Seq[R]] = executeQuery(query)(query.readR) - override def fetchHeadWithStatus[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] = { - executeQueryWithStatusHandling(query)(query.readStatusWithDataR) + override def runWithStatus[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] = { + executeQueryWithStatus(query)(query.readStatusWithDataR) } } diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala index 985952f1..80bdae97 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala @@ -51,7 +51,7 @@ class SlickPgEngine(val db: Database)(implicit val executor: ExecutionContext) e db.run(slickAction) } - override def fetchHeadWithStatus[R](query: QueryWithStatusType[R]): Future[Either[StatusException, R]] = { + override def runWithStatus[R](query: QueryWithStatusType[R]): Future[Either[StatusException, R]] = { val slickAction = query.sql.as[Either[StatusException, R]](query.getStatusExceptionOrData).head db.run(slickAction) } From 88fce851527469af77ad5934c1c8ec4e746b2448 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 8 Dec 2023 13:57:26 +0100 Subject: [PATCH 30/90] docs doobie module --- .../za/co/absa/fadb/doobie/DoobieEngine.scala | 13 +++++++++++++ .../za/co/absa/fadb/doobie/DoobieQuery.scala | 18 ++++++++++++++++++ .../co/absa/fadb/doobie/StatusWithData.scala | 6 ++++++ 3 files changed, 37 insertions(+) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala index 27612fca..2551bfd9 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala @@ -53,6 +53,13 @@ class DoobieEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DB query.fragment.query[R].to[Seq].transact(transactor) } + /** + * Executes a Doobie query and returns the result as an `F[Either[StatusException, R]]`. + * + * @param query the Doobie query to execute + * @param readStatusWithDataR the `Read[StatusWithData[R]]` instance used to read the query result into `StatusWithData[R]` + * @return the query result as an `F[Either[StatusException, R]]` + */ private def executeQueryWithStatus[R]( query: QueryWithStatusType[R] )(implicit readStatusWithDataR: Read[StatusWithData[R]]): F[Either[StatusException, R]] = { @@ -68,6 +75,12 @@ class DoobieEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DB override def run[R](query: QueryType[R]): F[Seq[R]] = executeQuery(query)(query.readR) + /** + * Runs a Doobie query and returns the result as an `F[Either[StatusException, R]]`. + * + * @param query the Doobie query to run + * @return the query result as an `F[Either[StatusException, R]]` + */ override def runWithStatus[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] = { executeQueryWithStatus(query)(query.readStatusWithDataR) } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala index 4d75fd99..96163e93 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala @@ -31,14 +31,32 @@ import za.co.absa.fadb.{FunctionStatusWithData, Query, QueryWithStatus} */ class DoobieQuery[R: Read](val fragment: Fragment)(implicit val readR: Read[R]) extends Query[R] +/** + * `DoobieQueryWithStatus` is a class that extends `QueryWithStatus` with `R` as the result type. + * It uses Doobie's `Fragment` to represent SQL queries. + * + * @param fragment the Doobie fragment representing the SQL query + * @param checkStatus the function to check the status of the query + * @param readStatusWithDataR the `Read[StatusWithData[R]]` instance used to read the query result into `StatusWithData[R]` + */ class DoobieQueryWithStatus[R]( val fragment: Fragment, checkStatus: FunctionStatusWithData[R] => Either[StatusException, R] )(implicit val readStatusWithDataR: Read[StatusWithData[R]]) extends QueryWithStatus[StatusWithData[R], R, R] { + /* + * Processes the status of the query and returns the status with data + * @param initialResult - the initial result of the query + * @return the status with data + */ override def processStatus(initialResult: StatusWithData[R]): FunctionStatusWithData[R] = FunctionStatusWithData(FunctionStatus(initialResult.status, initialResult.status_text), initialResult.data) + /* + * Converts the status with data to either a status exception or the data + * @param statusWithData - the status with data + * @return either a status exception or the data + */ override def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[R]): Either[StatusException, R] = checkStatus(statusWithData) } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala index 3f37d33c..1e7bca01 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala @@ -1,3 +1,9 @@ package za.co.absa.fadb.doobie +/** + * Represents a function status with data. + * @param functionStatus the function status + * @param data the data + * @tparam A the type of the data + */ case class StatusWithData[R](status: Int, status_text: String, data: R) From 299dde52d667a8b037edfa2cf31eb9d40bdbb237 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 8 Dec 2023 14:37:32 +0100 Subject: [PATCH 31/90] reduce the amount of docs in dbfunction --- .../main/scala/za/co/absa/fadb/DBEngine.scala | 9 ++++---- .../scala/za/co/absa/fadb/DBFunction.scala | 21 ------------------- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala index 94348f5a..27a74a29 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala @@ -23,15 +23,17 @@ import za.co.absa.fadb.exceptions.StatusException import scala.language.higherKinds /** - * A basis to represent a database executor + * `DBEngine` is an abstract class that represents a database engine. + * It provides methods to execute queries and fetch results from a database. + * @tparam F - The type of the context in which the database queries are executed. */ abstract class DBEngine[F[_]: Monad] { /** * A type representing the (SQL) query within the engine - * @tparam T - the return type of the query + * @tparam R - the return type of the query */ - type QueryType[T] <: Query[T] + type QueryType[R] <: Query[R] type QueryWithStatusType[R] <: QueryWithStatus[_, _, R] /** @@ -76,7 +78,6 @@ abstract class DBEngine[F[_]: Monad] { * @tparam R - return the of the query * @return - sequence of the results of database query */ - def fetchHeadOption[R](query: QueryType[R]): F[Option[R]] = { run(query).map(_.headOption) } diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 67e62dd4..ae074a18 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -136,13 +136,6 @@ object DBFunction { /** * `DBMultipleResultFunction` is an abstract class that represents a database function returning multiple results. * It extends the [[DBFunction]] class and overrides the apply method to return a sequence of results. - * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. - * @param schema - The schema the function belongs to. - * @param dBEngine - The database engine that is supposed to execute the function (presumably contains connection to the database). - * @tparam I - The type covering the input fields of the database function. - * @tparam R - The type covering the returned fields from the database function. - * @tparam E - The type of the [[DBEngine]] engine. - * @tparam F - The type of the context in which the database function is executed. */ abstract class DBMultipleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( functionNameOverride: Option[String] = None @@ -167,13 +160,6 @@ object DBFunction { /** * `DBSingleResultFunction` is an abstract class that represents a database function returning a single result. * It extends the [[DBFunction]] class and overrides the apply method to return a single result. - * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. - * @param schema - The schema the function belongs to. - * @param dBEngine - The database engine that is supposed to execute the function (presumably contains connection to the database). - * @tparam I - The type covering the input fields of the database function. - * @tparam R - The type covering the returned fields from the database function. - * @tparam E - The type of the [[DBEngine]] engine. - * @tparam F - The type of the context in which the database function is executed. */ abstract class DBSingleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( functionNameOverride: Option[String] = None @@ -197,13 +183,6 @@ object DBFunction { /** * `DBOptionalResultFunction` is an abstract class that represents a database function returning an optional result. * It extends the [[DBFunction]] class and overrides the apply method to return an optional result. - * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. - * @param schema - The schema the function belongs to. - * @param dBEngine - The database engine that is supposed to execute the function (presumably contains connection to the database). - * @tparam I - The type covering the input fields of the database function. - * @tparam R - The type covering the returned fields from the database function. - * @tparam E - The type of the [[DBEngine]] engine. - * @tparam F - The type of the context in which the database function is executed. */ abstract class DBOptionalResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( functionNameOverride: Option[String] = None From 0c58a68dc491011c56c96fe187602b0f34cd90c9 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 8 Dec 2023 19:22:28 +0100 Subject: [PATCH 32/90] minors --- .../main/scala/za/co/absa/fadb/DBEngine.scala | 6 +- .../scala/za/co/absa/fadb/DBFunction.scala | 133 +++++++++-------- .../co/absa/fadb/FunctionStatusWithData.scala | 8 +- .../main/scala/za/co/absa/fadb/Query.scala | 19 +-- .../fadb/exceptions/NamingException.scala | 1 - .../fadb/exceptions/StatusException.scala | 12 +- .../fadb/naming/ExplicitNamingRequired.scala | 16 +- .../za/co/absa/fadb/naming/LettersCase.scala | 20 +-- .../absa/fadb/naming/NamingConvention.scala | 26 ++-- .../naming/implementations/AsIsNaming.scala | 17 ++- .../implementations/SnakeCaseNaming.scala | 17 ++- .../co/absa/fadb/status/FunctionStatus.scala | 2 - ...ing.scala => StandardStatusHandling.scala} | 13 +- .../UserDefinedStatusHandling.scala | 35 +++++ .../za/co/absa/fadb/DBFunctionSuite.scala | 140 +++++++++--------- ...scala => StandardStatusHandlingTest.scala} | 4 +- ...ieSingleResultFunctionWithStatusTest.scala | 4 +- .../co/absa/fadb/doobie/DoobieFunction.scala | 24 +-- .../za/co/absa/fadb/doobie/DoobieQuery.scala | 5 +- .../co/absa/fadb/doobie/StatusWithData.scala | 5 +- .../examples/enceladus/DatasetSchema.scala | 30 ++-- ...ckSingleResultFunctionWithStatusTest.scala | 4 +- .../za/co/absa/fadb/slick/SlickPgEngine.scala | 6 + .../za/co/absa/fadb/slick/SlickQuery.scala | 8 +- 24 files changed, 289 insertions(+), 266 deletions(-) rename core/src/main/scala/za/co/absa/fadb/status/handling/implementations/{StandardQueryStatusHandling.scala => StandardStatusHandling.scala} (59%) create mode 100644 core/src/main/scala/za/co/absa/fadb/status/handling/implementations/UserDefinedStatusHandling.scala rename core/src/test/scala/za/co/absa/fadb/status/handling/implementations/{StandardQueryStatusHandlingTest.scala => StandardStatusHandlingTest.scala} (96%) diff --git a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala index 27a74a29..fdc08328 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala @@ -23,9 +23,9 @@ import za.co.absa.fadb.exceptions.StatusException import scala.language.higherKinds /** - * `DBEngine` is an abstract class that represents a database engine. - * It provides methods to execute queries and fetch results from a database. - * @tparam F - The type of the context in which the database queries are executed. + * `DBEngine` is an abstract class that represents a database engine. + * It provides methods to execute queries and fetch results from a database. + * @tparam F - The type of the context in which the database queries are executed. */ abstract class DBEngine[F[_]: Monad] { diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index ae074a18..32784a23 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -18,22 +18,22 @@ package za.co.absa.fadb import cats.Monad import za.co.absa.fadb.exceptions.StatusException -import za.co.absa.fadb.status.handling.QueryStatusHandling +import za.co.absa.fadb.status.handling.StatusHandling import scala.language.higherKinds /** - * `DBFunction` is an abstract class that represents a database function. - * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. - * @param schema - The schema the function belongs to. - * @param dBEngine - The database engine that is supposed to execute the function (contains connection to the database). - * @tparam I - The type covering the input fields of the database function. - * @tparam R - The type covering the returned fields from the database function. - * @tparam E - The type of the [[DBEngine]] engine. - * @tparam F - The type of the context in which the database function is executed. + * `DBFunction` is an abstract class that represents a database function. + * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. + * @param schema - The schema the function belongs to. + * @param dBEngine - The database engine that is supposed to execute the function (contains connection to the database). + * @tparam I - The type covering the input fields of the database function. + * @tparam R - The type covering the returned fields from the database function. + * @tparam E - The type of the [[DBEngine]] engine. + * @tparam F - The type of the context in which the database function is executed. */ -abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None)(implicit - override val schema: DBSchema, +abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None)( + implicit override val schema: DBSchema, val dBEngine: E ) extends DBFunctionFabric(functionNameOverride) { @@ -44,53 +44,55 @@ abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverr def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) /** - * Function to create the DB function call specific to the provided [[DBEngine]]. - * Expected to be implemented by the DBEngine specific mix-in. - * @param values - The values to pass over to the database function. - * @return - The SQL query in the format specific to the provided [[DBEngine]]. - */ - protected def query(values: I): dBEngine.QueryType[R] - - /** - * Executes the database function and returns multiple results. - * @param values - The values to pass over to the database function. - * @return - A sequence of results from the database function. + * Executes the database function and returns multiple results. + * @param values - The values to pass over to the database function. + * @return - A sequence of results from the database function. */ protected def multipleResults(values: I): F[Seq[R]] = dBEngine.fetchAll(query(values)) /** - * Executes the database function and returns a single result. - * @param values - The values to pass over to the database function. - * @return - A single result from the database function. + * Executes the database function and returns a single result. + * @param values - The values to pass over to the database function. + * @return - A single result from the database function. */ protected def singleResult(values: I): F[R] = dBEngine.fetchHead(query(values)) /** - * Executes the database function and returns an optional result. - * @param values - The values to pass over to the database function. - * @return - An optional result from the database function. + * Executes the database function and returns an optional result. + * @param values - The values to pass over to the database function. + * @return - An optional result from the database function. */ protected def optionalResult(values: I): F[Option[R]] = dBEngine.fetchHeadOption(query(values)) + + /** + * Function to create the DB function call specific to the provided [[DBEngine]]. + * Expected to be implemented by the DBEngine specific mix-in. + * @param values - The values to pass over to the database function. + * @return - The SQL query in the format specific to the provided [[DBEngine]]. + */ + protected def query(values: I): dBEngine.QueryType[R] } /** - * `DBFunctionWithStatus` is an abstract class that represents a database function with a status. - * It extends the [[DBFunction]] class and adds handling for the status of the function invocation. - * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. - * @param schema - The schema the function belongs to. - * @param dBEngine - The database engine that is supposed to execute the function (contains connection to the database). - * @param queryStatusHandling - The [[QueryStatusHandling]] instance that handles the status of the function invocation. - * @tparam I - The type covering the input fields of the database function. - * @tparam R - The type covering the returned fields from the database function. - * @tparam E - The type of the [[DBEngine]] engine. - * @tparam F - The type of the context in which the database function is executed. + * `DBFunctionWithStatus` is an abstract class that represents a database function with a status. + * It extends the [[DBFunction]] class and adds handling for the status of the function invocation. + * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. + * @param schema - The schema the function belongs to. + * @param dBEngine - The database engine that is supposed to execute the function (contains connection to the database). + * @param queryStatusHandling - The [[QueryStatusHandling]] instance that handles the status of the function invocation. + * @tparam I - The type covering the input fields of the database function. + * @tparam R - The type covering the returned fields from the database function. + * @tparam E - The type of the [[DBEngine]] engine. + * @tparam F - The type of the context in which the database function is executed. */ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None)( - implicit - override val schema: DBSchema, + implicit override val schema: DBSchema, val dBEngine: E ) extends DBFunctionFabric(functionNameOverride) - with QueryStatusHandling { + with StatusHandling { + + private val defaultStatusField = "status" + private val defaultStatusTextField = "statusText" // A constructor that takes only the mandatory parameters and uses default values for the optional ones def this()(implicit schema: DBSchema, dBEngine: E) = this(None) @@ -99,23 +101,12 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functio def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) /** - * Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the - * DBEngine specific mix-in. - * @param values the values to pass over to the database function - * @return the SQL query in the format specific to the provided [[DBEngine]] - */ - protected def query(values: I): dBEngine.QueryWithStatusType[R] - - /** - * Executes the database function and returns multiple results. - * @param values - * @return A sequence of results from the database function. + * Executes the database function and returns multiple results. + * @param values + * @return A sequence of results from the database function. */ def apply(values: I): F[Either[StatusException, R]] = dBEngine.runWithStatus(query(values)) - val defaultStatusField = "status" - val defaultStatusTextField = "statusText" - /** * The fields to select from the database function call * @return the fields to select from the database function call @@ -127,16 +118,24 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functio ) ++ super.fieldsToSelect } + /** + * Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the + * DBEngine specific mix-in. + * @param values the values to pass over to the database function + * @return the SQL query in the format specific to the provided [[DBEngine]] + */ + protected def query(values: I): dBEngine.QueryWithStatusType[R] + // To be provided by an implementation of QueryStatusHandling override def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] } object DBFunction { -/** - * `DBMultipleResultFunction` is an abstract class that represents a database function returning multiple results. - * It extends the [[DBFunction]] class and overrides the apply method to return a sequence of results. - */ + /** + * `DBMultipleResultFunction` is an abstract class that represents a database function returning multiple results. + * It extends the [[DBFunction]] class and overrides the apply method to return a sequence of results. + */ abstract class DBMultipleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( functionNameOverride: Option[String] = None )(implicit schema: DBSchema, dBEngine: E) @@ -157,10 +156,10 @@ object DBFunction { def apply(values: I): F[Seq[R]] = multipleResults(values) } -/** - * `DBSingleResultFunction` is an abstract class that represents a database function returning a single result. - * It extends the [[DBFunction]] class and overrides the apply method to return a single result. - */ + /** + * `DBSingleResultFunction` is an abstract class that represents a database function returning a single result. + * It extends the [[DBFunction]] class and overrides the apply method to return a single result. + */ abstract class DBSingleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( functionNameOverride: Option[String] = None )(implicit schema: DBSchema, dBEngine: E) @@ -180,10 +179,10 @@ object DBFunction { def apply(values: I): F[R] = singleResult(values) } -/** - * `DBOptionalResultFunction` is an abstract class that represents a database function returning an optional result. - * It extends the [[DBFunction]] class and overrides the apply method to return an optional result. - */ + /** + * `DBOptionalResultFunction` is an abstract class that represents a database function returning an optional result. + * It extends the [[DBFunction]] class and overrides the apply method to return an optional result. + */ abstract class DBOptionalResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( functionNameOverride: Option[String] = None )(implicit schema: DBSchema, dBEngine: E) diff --git a/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala b/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala index f2000d62..e210eb25 100644 --- a/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala +++ b/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala @@ -3,9 +3,9 @@ package za.co.absa.fadb import za.co.absa.fadb.status.FunctionStatus /** - * Represents a function status with data. - * @param functionStatus the function status - * @param data the data - * @tparam A the type of the data + * Represents a function status with data. + * @param functionStatus the function status + * @param data the data + * @tparam A the type of the data */ case class FunctionStatusWithData[A](functionStatus: FunctionStatus, data: A) diff --git a/core/src/main/scala/za/co/absa/fadb/Query.scala b/core/src/main/scala/za/co/absa/fadb/Query.scala index 0bac83da..99af5743 100644 --- a/core/src/main/scala/za/co/absa/fadb/Query.scala +++ b/core/src/main/scala/za/co/absa/fadb/Query.scala @@ -29,24 +29,25 @@ trait Query[R] * @tparam R - the return type of the query */ trait QueryWithStatus[A, B, R] { + /** - * Processes the status of the query and returns the status with data - * @param initialResult - the initial result of the query - * @return the status with data + * Processes the status of the query and returns the status with data + * @param initialResult - the initial result of the query + * @return the status with data */ def processStatus(initialResult: A): FunctionStatusWithData[B] /** - * Converts the status with data to either a status exception or the data - * @param statusWithData - the status with data - * @return either a status exception or the data + * Converts the status with data to either a status exception or the data + * @param statusWithData - the status with data + * @return either a status exception or the data */ def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[B]): Either[StatusException, R] /** - * Returns the result of the query or a status exception - * @param initialResult - the initial result of the query - * @return the result of the query or a status exception + * Returns the result of the query or a status exception + * @param initialResult - the initial result of the query + * @return the result of the query or a status exception */ def getResultOrException(initialResult: A): Either[StatusException, R] = toStatusExceptionOrData( processStatus(initialResult) diff --git a/core/src/main/scala/za/co/absa/fadb/exceptions/NamingException.scala b/core/src/main/scala/za/co/absa/fadb/exceptions/NamingException.scala index 97d6ef34..4d4c4d56 100644 --- a/core/src/main/scala/za/co/absa/fadb/exceptions/NamingException.scala +++ b/core/src/main/scala/za/co/absa/fadb/exceptions/NamingException.scala @@ -18,6 +18,5 @@ package za.co.absa.fadb.exceptions /** * Exception thrown when a naming convention is not found for a given string - * @param message - the message to display */ case class NamingException(message: String) extends Exception(message) diff --git a/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala b/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala index 560747fe..f98ee511 100644 --- a/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala +++ b/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala @@ -8,31 +8,31 @@ import za.co.absa.fadb.status.FunctionStatus sealed abstract class StatusException(val status: FunctionStatus) extends Exception(status.statusText) /** - * Represents an exception that is returned when there is a server misconfiguration. + * Represents an exception that is returned when there is a server misconfiguration. */ final case class ServerMisconfigurationException(override val status: FunctionStatus) extends StatusException(status) /** - * Represents an exception that is returned when there is a data conflict. + * Represents an exception that is returned when there is a data conflict. */ final case class DataConflictException(override val status: FunctionStatus) extends StatusException(status) /** - * Represents an exception that is returned when data is not found. + * Represents an exception that is returned when data is not found. */ final case class DataNotFoundException(override val status: FunctionStatus) extends StatusException(status) /** - * Represents an exception that is returned when there is an error in data. + * Represents an exception that is returned when there is an error in data. */ final case class ErrorInDataException(override val status: FunctionStatus) extends StatusException(status) /** - * Represents an exception that is returned for other statuses. + * Represents an exception that is returned for other statuses. */ final case class OtherStatusException(override val status: FunctionStatus) extends StatusException(status) /** - * Represents an exception that is returned when the status is out of range. + * Represents an exception that is returned when the status is out of range. */ final case class StatusOutOfRangeException(override val status: FunctionStatus) extends StatusException(status) diff --git a/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala b/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala index 98d1b5f9..5238387f 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala @@ -18,17 +18,16 @@ package za.co.absa.fadb.naming import za.co.absa.fadb.exceptions.NamingException - /** - * `ExplicitNamingRequired` is a [[NamingConvention]] that throws a [[NamingException]] for any string. - * This is used when explicit naming is required and no other naming convention should be applied. + * `ExplicitNamingRequired` is a [[NamingConvention]] that throws a [[NamingException]] for any string. + * This is used when explicit naming is required and no other naming convention should be applied. */ class ExplicitNamingRequired extends NamingConvention { /** - * Throws a [[NamingException]] with a message indicating that explicit naming is required. - * @param original - The original string. - * @return Nothing, as a [[NamingException]] is always thrown. + * Throws a [[NamingException]] with a message indicating that explicit naming is required. + * @param original - The original string. + * @return Nothing, as a [[NamingException]] is always thrown. */ override def stringPerConvention(original: String): String = { val message = s"No convention for '$original', explicit naming required." @@ -37,12 +36,13 @@ class ExplicitNamingRequired extends NamingConvention { } /** - * `ExplicitNamingRequired.Implicits` provides an implicit [[NamingConvention]] instance that throws a [[NamingException]] for any string. + * `ExplicitNamingRequired.Implicits` provides an implicit [[NamingConvention]] instance that throws a [[NamingException]] for any string. */ object ExplicitNamingRequired { object Implicits { + /** - * An implicit [[NamingConvention]] instance that throws a [[NamingException]] for any string. + * An implicit [[NamingConvention]] instance that throws a [[NamingException]] for any string. */ implicit val namingConvention: NamingConvention = new ExplicitNamingRequired() } diff --git a/core/src/main/scala/za/co/absa/fadb/naming/LettersCase.scala b/core/src/main/scala/za/co/absa/fadb/naming/LettersCase.scala index f5554d9b..471dc1c1 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/LettersCase.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/LettersCase.scala @@ -17,35 +17,37 @@ package za.co.absa.fadb.naming /** - * `LettersCase` is a sealed trait that represents different cases of letters. - * It provides a method to convert a string to the specific case. + * `LettersCase` is a sealed trait that represents different cases of letters. + * It provides a method to convert a string to the specific case. */ sealed trait LettersCase { + /** - * Converts a string to the specific case. - * @param s - The original string. - * @return The string converted to the specific case. + * Converts a string to the specific case. + * @param s - The original string. + * @return The string converted to the specific case. */ def convert(s: String): String } object LettersCase { + /** - * `AsIs` is a [[LettersCase]] that leaves strings as they are. - */ + * `AsIs` is a [[LettersCase]] that leaves strings as they are. + */ case object AsIs extends LettersCase { override def convert(s: String): String = s } /** - * `LowerCase` is a [[LettersCase]] that converts strings to lower case. + * `LowerCase` is a [[LettersCase]] that converts strings to lower case. */ case object LowerCase extends LettersCase { override def convert(s: String): String = s.toLowerCase } /** - * `UpperCase` is a [[LettersCase]] that converts strings to upper case. + * `UpperCase` is a [[LettersCase]] that converts strings to upper case. */ case object UpperCase extends LettersCase { override def convert(s: String): String = s.toUpperCase diff --git a/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala b/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala index cfb27213..b5adadfa 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala @@ -17,16 +17,16 @@ package za.co.absa.fadb.naming /** - * `NamingConvention` is a base trait that defines the interface for different naming conventions. - * It provides methods to convert a class name according to given naming convention. + * `NamingConvention` is a base trait that defines the interface for different naming conventions. + * It provides methods to convert a class name according to given naming convention. */ trait NamingConvention { /** - * Converts the class name according to the specific naming convention. - * @param c - The class. - * @return The class name converted to string according to the specific naming convention. - */ + * Converts the class name according to the specific naming convention. + * @param c - The class. + * @return The class name converted to string according to the specific naming convention. + */ def fromClassNamePerConvention(c: Class[_]): String = { val className = c.getSimpleName val cleanClassName = className.lastIndexOf('$') match { @@ -37,18 +37,18 @@ trait NamingConvention { } /** - * Converts the class name according to the specific naming convention. - * @param instance - The instance of the class. - * @return The class name converted to string according to the specific naming convention. - */ + * Converts the class name according to the specific naming convention. + * @param instance - The instance of the class. + * @return The class name converted to string according to the specific naming convention. + */ def fromClassNamePerConvention(instance: AnyRef): String = { fromClassNamePerConvention(instance.getClass) } /** - * Converts the original string according to the specific naming convention. - * @param original - The original string. - * @return The original string converted according to the specific naming convention. + * Converts the original string according to the specific naming convention. + * @param original - The original string. + * @return The original string converted according to the specific naming convention. */ def stringPerConvention(original: String): String } diff --git a/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala b/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala index 6e31e925..926cd00c 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala @@ -20,16 +20,16 @@ import za.co.absa.fadb.naming.{LettersCase, NamingConvention} import LettersCase.AsIs /** - * `AsIsNaming` provides a naming convention that leaves strings as they are. - * It implements the [[NamingConvention]] trait. - * @param lettersCase - The case of the letters in the string. + * `AsIsNaming` provides a naming convention that leaves strings as they are. + * It implements the [[NamingConvention]] trait. + * @param lettersCase - The case of the letters in the string. */ class AsIsNaming(lettersCase: LettersCase) extends NamingConvention { /** - * Returns the original string converted to the specified letter case. - * @param original - The original string. - * @return The original string converted to the specified letter case. + * Returns the original string converted to the specified letter case. + * @param original - The original string. + * @return The original string converted to the specified letter case. */ override def stringPerConvention(original: String): String = { lettersCase.convert(original) @@ -37,12 +37,13 @@ class AsIsNaming(lettersCase: LettersCase) extends NamingConvention { } /** - * `AsIsNaming.Implicits` provides an implicit [[NamingConvention]] instance that leaves strings as they are. + * `AsIsNaming.Implicits` provides an implicit [[NamingConvention]] instance that leaves strings as they are. */ object AsIsNaming { object Implicits { + /** - * An implicit [[NamingConvention]] instance that leaves strings as they are. + * An implicit [[NamingConvention]] instance that leaves strings as they are. */ implicit val namingConvention: NamingConvention = new AsIsNaming(AsIs) } diff --git a/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala b/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala index 394d3cc4..7362ea2c 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala @@ -20,9 +20,9 @@ import za.co.absa.fadb.naming.{LettersCase, NamingConvention} import LettersCase.LowerCase /** - * `SnakeCaseNaming` provides a naming convention that converts camel case strings to snake case. - * It implements the [[NamingConvention]] trait. - * @param lettersCase - The case of the letters in the string. + * `SnakeCaseNaming` provides a naming convention that converts camel case strings to snake case. + * It implements the [[NamingConvention]] trait. + * @param lettersCase - The case of the letters in the string. */ class SnakeCaseNaming(lettersCase: LettersCase) extends NamingConvention { @@ -41,9 +41,9 @@ class SnakeCaseNaming(lettersCase: LettersCase) extends NamingConvention { } /** - * Converts the original string to snake case and the specified letter case. - * @param original - The original string. - * @return The original string converted to snake case and the specified letter case. + * Converts the original string to snake case and the specified letter case. + * @param original - The original string. + * @return The original string converted to snake case and the specified letter case. */ override def stringPerConvention(original: String): String = { lettersCase.convert(stripIfFirstChar(camelCaseToSnakeCase(original), '_')) @@ -51,12 +51,13 @@ class SnakeCaseNaming(lettersCase: LettersCase) extends NamingConvention { } /** - * `SnakeCaseNaming.Implicits` provides an implicit [[NamingConvention]] instance that converts camel case strings to snake case. + * `SnakeCaseNaming.Implicits` provides an implicit [[NamingConvention]] instance that converts camel case strings to snake case. */ object SnakeCaseNaming { object Implicits { + /** - * An implicit [[NamingConvention]] instance that converts camel case strings to snake case. + * An implicit [[NamingConvention]] instance that converts camel case strings to snake case. */ implicit val namingConvention: NamingConvention = new SnakeCaseNaming(LowerCase) } diff --git a/core/src/main/scala/za/co/absa/fadb/status/FunctionStatus.scala b/core/src/main/scala/za/co/absa/fadb/status/FunctionStatus.scala index b34ba93e..daa9de8c 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/FunctionStatus.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/FunctionStatus.scala @@ -18,7 +18,5 @@ package za.co.absa.fadb.status /** * Class represents the status of calling a fa-db function (if it supports status that is) - * @param statusCode - status code identifying if the function call succeeded or failed and how - * @param statusText - human readable description of the status returned */ case class FunctionStatus(statusCode: Int, statusText: String) diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala similarity index 59% rename from core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala rename to core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala index 5ceb67c9..579cf547 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala @@ -2,19 +2,16 @@ package za.co.absa.fadb.status.handling.implementations import za.co.absa.fadb.FunctionStatusWithData import za.co.absa.fadb.exceptions._ -import za.co.absa.fadb.status.handling.QueryStatusHandling +import za.co.absa.fadb.status.handling.StatusHandling /** - * `StandardQueryStatusHandling` is a trait that extends the [[QueryStatusHandling]] interface. - * It provides a standard implementation for checking the status of a function invocation. + * `StandardStatusHandling` is a trait that extends the [[StatusHandling]] interface. + * It provides a standard implementation for checking the status of a function invocation. */ -trait StandardQueryStatusHandling extends QueryStatusHandling { +trait StandardStatusHandling extends StatusHandling { /** - * Checks the status of a function invocation. - * @param statusWithData - The status of the function invocation with the data returned by the function. - * @tparam A - The type of the data returned by the function. - * @return Either the data returned by the function or an exception. + * Checks the status of a function invocation. */ override def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] = { val functionStatus = statusWithData.functionStatus diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/UserDefinedStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/UserDefinedStatusHandling.scala new file mode 100644 index 00000000..f7d8c665 --- /dev/null +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/UserDefinedStatusHandling.scala @@ -0,0 +1,35 @@ +package za.co.absa.fadb.status.handling.implementations + +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import za.co.absa.fadb.FunctionStatusWithData +import za.co.absa.fadb.exceptions.{OtherStatusException, StatusException} +import za.co.absa.fadb.status.handling.StatusHandling + +/** + * Trait represents user defined status handling + */ +trait UserDefinedStatusHandling extends StatusHandling { + def OKStatuses: Set[Integer] + + override def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] = + if (OKStatuses.contains(statusWithData.functionStatus.statusCode)) { + Right(statusWithData.data) + } else { + Left(OtherStatusException(statusWithData.functionStatus)) + } +} diff --git a/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala b/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala index 5c6cd2e3..2be72b6f 100644 --- a/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala @@ -1,69 +1,71 @@ -///* -// * Copyright 2022 ABSA Group Limited -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -// -//package za.co.absa.fadb -// -//import cats.implicits._ -//import org.scalatest.funsuite.AnyFunSuite -//import za.co.absa.fadb.DBFunction.DBSingleResultFunction -//import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention -// -//import scala.concurrent.ExecutionContext.Implicits.global -//import scala.concurrent.Future -// -//class DBFunctionSuite extends AnyFunSuite { -// -// private def neverHappens: Nothing = { -// throw new Exception("Should never get here") -// } -// -// class EngineThrow extends DBEngine[Future] { -// override def run[R](query: QueryType[R]): Future[Seq[R]] = neverHappens -// } -// -// private object FooNamed extends DBSchema -// private object FooNameless extends DBSchema("") -// -// test("Function name check"){ -// -// class MyFunction(functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, val dbEngine: EngineThrow) -// extends DBSingleResultFunction[Unit, Unit, EngineThrow, Future](None) { -// -// override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens -// } -// -// val fnc1 = new MyFunction()(FooNamed, new EngineThrow) -// val fnc2 = new MyFunction()(FooNameless, new EngineThrow) -// -// assert(fnc1.functionName == "foo_named.my_function") -// assert(fnc2.functionName == "my_function") -// } -// -// test("Function name override check"){ -// class MyFunction(implicit override val schema: DBSchema, val dbEngine: EngineThrow) -// extends DBSingleResultFunction[Unit, Unit, EngineThrow, Future](Some("bar")) { -// -// override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens -// } -// -// val fnc1 = new MyFunction()(FooNamed, new EngineThrow) -// val fnc2 = new MyFunction()(FooNameless, new EngineThrow) -// -// assert(fnc1.functionName == "foo_named.bar") -// assert(fnc2.functionName == "bar") -// } -// -//} +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.fadb + +import cats.implicits._ +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBFunction.DBSingleResultFunction +import za.co.absa.fadb.exceptions.StatusException +import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future + +class DBFunctionSuite extends AnyFunSuite { + + private def neverHappens: Nothing = { + throw new Exception("Should never get here") + } + + class EngineThrow extends DBEngine[Future] { + override def run[R](query: QueryType[R]): Future[Seq[R]] = neverHappens + override def runWithStatus[R](query: QueryWithStatusType[R]): Future[Either[StatusException, R]] = neverHappens + } + + private object FooNamed extends DBSchema + private object FooNameless extends DBSchema("") + + test("Function name check"){ + + class MyFunction(implicit override val schema: DBSchema, val dbEngine: EngineThrow) + extends DBSingleResultFunction[Unit, Unit, EngineThrow, Future](None) { + + override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens + } + + val fnc1 = new MyFunction()(FooNamed, new EngineThrow) + val fnc2 = new MyFunction()(FooNameless, new EngineThrow) + + assert(fnc1.functionName == "foo_named.my_function") + assert(fnc2.functionName == "my_function") + } + + test("Function name override check"){ + class MyFunction(implicit override val schema: DBSchema, val dbEngine: EngineThrow) + extends DBSingleResultFunction[Unit, Unit, EngineThrow, Future](Some("bar")) { + + override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens + } + + val fnc1 = new MyFunction()(FooNamed, new EngineThrow) + val fnc2 = new MyFunction()(FooNameless, new EngineThrow) + + assert(fnc1.functionName == "foo_named.bar") + assert(fnc2.functionName == "bar") + } + +} diff --git a/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandlingTest.scala b/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandlingTest.scala similarity index 96% rename from core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandlingTest.scala rename to core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandlingTest.scala index cc1c81b3..9ee504c5 100644 --- a/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardQueryStatusHandlingTest.scala +++ b/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandlingTest.scala @@ -6,9 +6,9 @@ import za.co.absa.fadb.FunctionStatusWithData import za.co.absa.fadb.exceptions._ import za.co.absa.fadb.status.FunctionStatus -class StandardQueryStatusHandlingTest extends AnyFunSuiteLike { +class StandardStatusHandlingTest extends AnyFunSuiteLike { - private val standardQueryStatusHandling = new StandardQueryStatusHandling {} + private val standardQueryStatusHandling = new StandardStatusHandling {} test("checkStatus should return Right when status code is in the range 10-19") { for (statusCode <- 10 to 19) { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala index 9addd436..1a80de03 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala @@ -24,13 +24,13 @@ import doobie.util.Read import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStatus -import za.co.absa.fadb.status.handling.implementations.StandardQueryStatusHandling +import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling class DoobieSingleResultFunctionWithStatusTest extends AnyFunSuite with DoobieTest { class CreateActor(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) extends DoobieSingleResultFunctionWithStatus[CreateActorRequestBody, Int, IO] - with StandardQueryStatusHandling { + with StandardStatusHandling { override def sql(values: CreateActorRequestBody)(implicit read: Read[StatusWithData[Int]]): Fragment = { sql"SELECT status, status_text, o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index 93544896..c92bde3e 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -106,8 +106,7 @@ object DoobieFunction { */ abstract class DoobieSingleResultFunctionWithStatus[I, R, F[_]: Async: Monad]( functionNameOverride: Option[String] = None - )(implicit - override val schema: DBSchema, + )(implicit override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R], val readSelectWithStatus: Read[StatusWithData[R]] @@ -125,8 +124,7 @@ object DoobieFunction { * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieSingleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit - override val schema: DBSchema, + implicit override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] ) extends DBSingleResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) @@ -135,16 +133,9 @@ object DoobieFunction { /** * `DoobieMultipleResultFunction` is an abstract class that extends `DBMultipleResultFunction` with `DoobiePgEngine` as the engine type. * It represents a database function that returns multiple results. - * - * @param functionNameOverride the optional override for the function name - * @param schema the database schema - * @param dbEngine the `DoobieEngine` instance used to execute SQL queries - * @param readR the `Read[R]` instance used to read the query result into `R` - * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieMultipleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit - override val schema: DBSchema, + implicit override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] ) extends DBMultipleResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) @@ -153,16 +144,9 @@ object DoobieFunction { /** * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` with `DoobiePgEngine` as the engine type. * It represents a database function that returns an optional result. - * - * @param functionNameOverride the optional override for the function name - * @param schema the database schema - * @param dbEngine the `DoobieEngine` instance used to execute SQL queries - * @param readR the `Read[R]` instance used to read the query result into `R` - * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ abstract class DoobieOptionalResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( - implicit - override val schema: DBSchema, + implicit override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] ) extends DBOptionalResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala index 96163e93..c6f868f9 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala @@ -29,7 +29,7 @@ import za.co.absa.fadb.{FunctionStatusWithData, Query, QueryWithStatus} * @param fragment the Doobie fragment representing the SQL query * @param readR the `Read[R]` instance used to read the query result into `R` */ -class DoobieQuery[R: Read](val fragment: Fragment)(implicit val readR: Read[R]) extends Query[R] +class DoobieQuery[R](val fragment: Fragment)(implicit val readR: Read[R]) extends Query[R] /** * `DoobieQueryWithStatus` is a class that extends `QueryWithStatus` with `R` as the result type. @@ -42,7 +42,8 @@ class DoobieQuery[R: Read](val fragment: Fragment)(implicit val readR: Read[R]) class DoobieQueryWithStatus[R]( val fragment: Fragment, checkStatus: FunctionStatusWithData[R] => Either[StatusException, R] -)(implicit val readStatusWithDataR: Read[StatusWithData[R]]) extends QueryWithStatus[StatusWithData[R], R, R] { +)(implicit val readStatusWithDataR: Read[StatusWithData[R]]) + extends QueryWithStatus[StatusWithData[R], R, R] { /* * Processes the status of the query and returns the status with data diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala index 1e7bca01..ce4e0da5 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala @@ -1,9 +1,6 @@ package za.co.absa.fadb.doobie /** - * Represents a function status with data. - * @param functionStatus the function status - * @param data the data - * @tparam A the type of the data + * Represents a function status with data. */ case class StatusWithData[R](status: Int, status_text: String, data: R) diff --git a/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala b/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala index 6765d6d7..b0100a31 100644 --- a/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala +++ b/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala @@ -16,19 +16,17 @@ package za.co.absa.fadb.examples.enceladus +import slick.jdbc.PostgresProfile.api._ +import slick.jdbc.{GetResult, SQLActionBuilder} import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.slick.{SlickFunction, SlickFunctionWithStatusSupport, SlickPgEngine} +import za.co.absa.fadb.examples.enceladus.DatasetSchema._ import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention -import slick.jdbc.{GetResult, SQLActionBuilder} -import slick.jdbc.PostgresProfile.api._ +import za.co.absa.fadb.slick.SlickFunction.{SlickMultipleResultFunction, SlickSingleResultFunctionWithStatus} +import za.co.absa.fadb.slick.SlickPgEngine +import za.co.absa.fadb.status.handling.implementations.UserDefinedStatusHandling import java.sql.Timestamp import scala.concurrent.Future -import DatasetSchema._ -import cats.implicits._ -import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBSingleResultFunction} - -import scala.concurrent.ExecutionContext.Implicits.global /* The Schema doesn't need the dBEngine directly, but it seems cleaner this way to hand it over to schema's functions */ class DatasetSchema(implicit engine: SlickPgEngine) extends DBSchema { @@ -63,9 +61,8 @@ object DatasetSchema { case class SchemaHeader(entityName: String, entityLatestVersion: Int) - final class AddSchema(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) - extends DBSingleResultFunction[SchemaInput, Long, SlickPgEngine, Future] - with SlickFunctionWithStatusSupport[SchemaInput, Long] + final class AddSchema(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) + extends SlickSingleResultFunctionWithStatus[SchemaInput, Long] with UserDefinedStatusHandling { override protected def sql(values: SchemaInput): SQLActionBuilder = { @@ -78,12 +75,10 @@ object DatasetSchema { override protected def slickConverter: GetResult[Long] = GetResult.GetLong override def OKStatuses: Set[Integer] = Set(201) - } - final class GetSchema(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) - extends DBSingleResultFunction[(String, Option[Int]), Schema, SlickPgEngine, Future] - with SlickFunctionWithStatusSupport[(String, Option[Int]), Schema] + final class GetSchema(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) + extends SlickSingleResultFunctionWithStatus[(String, Option[Int]), Schema] with UserDefinedStatusHandling { /* This is an example of how to deal with overloaded DB functions - see different input type: Long vs what's in the class type: (String, Option[Int]) */ @@ -108,9 +103,8 @@ object DatasetSchema { override val OKStatuses: Set[Integer] = Set(200) } - final class List(implicit override val schema: DBSchema, override val dbEngine: SlickPgEngine) - extends DBMultipleResultFunction[Boolean, SchemaHeader, SlickPgEngine, Future]() - with SlickFunction[Boolean, SchemaHeader] { + final class List(implicit override val schema: DBSchema, val dbEngine: SlickPgEngine) + extends SlickMultipleResultFunction[Boolean, SchemaHeader] { override def apply(values: Boolean = false): Future[Seq[SchemaHeader]] = super.apply(values) diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala index e4ebacf8..436b656b 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala @@ -6,14 +6,14 @@ import slick.jdbc.{GetResult, SQLActionBuilder} import za.co.absa.fadb.DBSchema import za.co.absa.fadb.slick.FaDbPostgresProfile.api._ import za.co.absa.fadb.slick.SlickFunction.SlickSingleResultFunctionWithStatus -import za.co.absa.fadb.status.handling.implementations.StandardQueryStatusHandling +import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling import scala.concurrent.ExecutionContext.Implicits.global class SlickSingleResultFunctionWithStatusTest extends AnyFunSuite with SlickTest with ScalaFutures { class CreateActor(implicit schema: DBSchema, dbEngine: SlickPgEngine) extends SlickSingleResultFunctionWithStatus[CreateActorRequestBody, Int] - with StandardQueryStatusHandling { + with StandardStatusHandling { override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("o_actor_id") diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala index 80bdae97..9963778b 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala @@ -51,6 +51,12 @@ class SlickPgEngine(val db: Database)(implicit val executor: ExecutionContext) e db.run(slickAction) } + /** + * Execution using Slick with status + * @param query - the Slick query to execute + * @tparam R - return the of the query + * @return - either status exception or result of database query + */ override def runWithStatus[R](query: QueryWithStatusType[R]): Future[Either[StatusException, R]] = { val slickAction = query.sql.as[Either[StatusException, R]](query.getStatusExceptionOrData).head db.run(slickAction) diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala index a7e47df4..16036067 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala @@ -30,7 +30,13 @@ import za.co.absa.fadb.{FunctionStatusWithData, Query, QueryWithStatus} */ class SlickQuery[R](val sql: SQLActionBuilder, val getResult: GetResult[R]) extends Query[R] - +/** + * SQL query representation for Slick with status + * @param sql - the SQL query in Slick format + * @param getResult - the converting function, that converts the [[slick.jdbc.PositionedResult slick.PositionedResult]] (the result of Slick + * execution) into the desire `R` type + * @tparam R - the return type of the query + */ class SlickQueryWithStatus[R]( val sql: SQLActionBuilder, val getResult: GetResult[R], From 84e30ef38aa3590659accf57b0e9bd53caa1538b Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 8 Dec 2023 19:22:37 +0100 Subject: [PATCH 33/90] minors --- .../status/handling/QueryStatusHandling.scala | 17 ----------------- .../fadb/status/handling/StatusHandling.scala | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 17 deletions(-) delete mode 100644 core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala create mode 100644 core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala deleted file mode 100644 index 95bb34aa..00000000 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/QueryStatusHandling.scala +++ /dev/null @@ -1,17 +0,0 @@ -package za.co.absa.fadb.status.handling - -import za.co.absa.fadb.FunctionStatusWithData -import za.co.absa.fadb.exceptions.StatusException - -/** - * `QueryStatusHandling` is a base trait that defines the interface for handling the status of a function invocation. - * It provides a method to check the status of a function invocation with data. - */ -trait QueryStatusHandling { - /** - * Checks the status of a function invocation. - * @param statusWithData - The status of the function invocation with data. - * @return Either a [[StatusException]] if the status code indicates an error, or the data if the status code is successful. - */ - def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] -} diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala new file mode 100644 index 00000000..7f104e36 --- /dev/null +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala @@ -0,0 +1,18 @@ +package za.co.absa.fadb.status.handling + +import za.co.absa.fadb.FunctionStatusWithData +import za.co.absa.fadb.exceptions.StatusException + +/** + * `StatusHandling` is a base trait that defines the interface for handling the status of a function invocation. + * It provides a method to check the status of a function invocation with data. + */ +trait StatusHandling { + + /** + * Checks the status of a function invocation. + * @param statusWithData - The status of the function invocation with data. + * @return Either a [[StatusException]] if the status code indicates an error, or the data if the status code is successful. + */ + def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] +} From 5f95411926ac33b52a552a1e3d0d9833b072c6f1 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 8 Dec 2023 19:30:29 +0100 Subject: [PATCH 34/90] slick function docs --- .../za/co/absa/fadb/slick/SlickFunction.scala | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala index 15ac5540..0df6d0f1 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala @@ -25,6 +25,12 @@ import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, FunctionStatusWithData} import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future +/** + * Base class for Slick DB functions. + * + * @tparam I the input type of the function + * @tparam R the result type of the function + */ private[slick] trait SlickFunctionBase[I, R] { /** @@ -80,38 +86,51 @@ private[slick] trait SlickFunction[I, R] extends SlickFunctionBase[I, R] { private[slick] trait SlickFunctionWithStatus[I, R] extends SlickFunctionBase[I, R] { - def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] - /** - * Generates a `SlickQueryWithStatus[R]` representing the SQL query for the function with status support. + * Generates a `SlickQueryWithStatus[R]` representing the SQL query for the function with status support. * - * @param status - the status to check - * @return - Success or failure the status means + * @param status - the status to check + * @return - Success or failure the status means */ protected def query(values: I): SlickQueryWithStatus[R] = new SlickQueryWithStatus[R](sql(values), slickConverter, checkStatus) + + // Expected to be mixed in by an implementation of StatusHandling + def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] } object SlickFunction { + /** + * Class for Slick DB functions with status support. + */ abstract class SlickSingleResultFunctionWithStatus[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, DBEngine: SlickPgEngine ) extends DBFunctionWithStatus[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunctionWithStatus[I, R] + /** + * Class for Slick DB functions with single result. + */ abstract class SlickSingleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, DBEngine: SlickPgEngine ) extends DBSingleResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunction[I, R] + /** + * Class for Slick DB functions with multiple results. + */ abstract class SlickMultipleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, DBEngine: SlickPgEngine ) extends DBMultipleResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunction[I, R] + /** + * Class for Slick DB functions with optional result. + */ abstract class SlickOptionalResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, DBEngine: SlickPgEngine From 6cd018d24048c658c33395f5367041e346d4bd78 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 8 Dec 2023 23:06:06 +0100 Subject: [PATCH 35/90] selectEntry available for both Doobie and Slick modules --- .../za/co/absa/fadb/DBFunctionFabric.scala | 30 +++++++++++++++---- .../DoobieSingleResultFunctionTest.scala | 5 ++++ .../co/absa/fadb/doobie/DoobieFunction.scala | 9 ++---- .../za/co/absa/fadb/slick/SlickFunction.scala | 28 ----------------- 4 files changed, 33 insertions(+), 39 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala b/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala index bb607e51..60f2f623 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala @@ -21,15 +21,13 @@ package za.co.absa.fadb * that offer certain implementations. This trait should help with the inheritance of all of these */ abstract class DBFunctionFabric(functionNameOverride: Option[String])(implicit val schema: DBSchema) { - /** - * List of fields to select from the DB function. - * @return - list of fields to select + * Alias of the function, based on the class name */ - def fieldsToSelect: Seq[String] = Seq.empty + protected val alias = "FNC" /** - * Name of the function, based on the class name, unless it is overridden in the constructor + * Name of the function, based on the class name, unless it is overridden in the constructor */ val functionName: String = { val fn = functionNameOverride.getOrElse(schema.objectNameFromClassName(getClass)) @@ -40,4 +38,26 @@ abstract class DBFunctionFabric(functionNameOverride: Option[String])(implicit v } } + /** + * List of fields to select from the DB function. + * @return - list of fields to select + */ + def fieldsToSelect: Seq[String] = Seq.empty + + /* + * Generates a list of select columns for the function + */ + protected def selectEntry: String = { + val fieldsSeq = fieldsToSelect + if (fieldsSeq.isEmpty) { + "*" + } else { + val aliasToUse = if (alias.isEmpty) { + "" + } else { + s"$alias." + } + fieldsToSelect.map(aliasToUse + _).mkString(",") + } + } } diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala index 6a9f3615..0e5d80de 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala @@ -30,8 +30,13 @@ class DoobieSingleResultFunctionTest extends AnyFunSuite with DoobieTest { class CreateActor(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) extends DoobieSingleResultFunction[CreateActorRequestBody, Int, IO] { + // do not remove the example below + // override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("o_actor_id") + override def sql(values: CreateActorRequestBody)(implicit read: Read[Int]): Fragment = sql"SELECT o_actor_id FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" + // do not remove the example below, it has to be used with the override def fieldsToSelect + // sql"SELECT ${Fragment.const(selectEntry)} FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName}) ${Fragment.const(alias)}" } private val createActor = new CreateActor()(Runs, new DoobieEngine(transactor)) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index c92bde3e..5b956768 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -27,7 +27,6 @@ import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, FunctionStatusWithData} import scala.language.higherKinds trait DoobieFunctionBase[R] { - /** * The `Read[R]` instance used to read the query result into `R`. */ @@ -41,7 +40,6 @@ trait DoobieFunctionBase[R] { * @tparam R the result type of the function */ private[doobie] trait DoobieFunction[I, R] extends DoobieFunctionBase[R] { - /** * Generates a Doobie `Fragment` representing the SQL query for the function. * @@ -60,9 +58,6 @@ private[doobie] trait DoobieFunction[I, R] extends DoobieFunctionBase[R] { } private[doobie] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBase[R] { - - def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] - /** * The `Read[StatusWithData[R]]` instance used to read the query result with status into `StatusWithData[R]`. */ @@ -85,6 +80,9 @@ private[doobie] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBase[ * @return the `DoobieQueryWithStatus[R]` representing the SQL query */ protected def query(values: I): DoobieQueryWithStatus[R] = new DoobieQueryWithStatus[R](sql(values), checkStatus) + + // This is to be mixed in by an implementation of StatusHandling + def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] } /** @@ -92,7 +90,6 @@ private[doobie] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBase[ * These classes use Doobie's `Fragment` to represent SQL queries and `DoobieEngine` to execute them. */ object DoobieFunction { - /** * `DoobieSingleResultFunctionWithStatus` is an abstract class that extends `DBSingleResultFunctionWithStatus` with `DoobiePgEngine` as the engine type. * It represents a database function that returns a single result with status. diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala index 0df6d0f1..668039cf 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala @@ -32,7 +32,6 @@ import scala.concurrent.Future * @tparam R the result type of the function */ private[slick] trait SlickFunctionBase[I, R] { - /** * The `GetResult[R]` instance used to read the query result into `R`. */ @@ -47,34 +46,9 @@ private[slick] trait SlickFunctionBase[I, R] { protected def sql(values: I): SQLActionBuilder def fieldsToSelect: Seq[String] - - /** - * Alias to use within the SQL query - */ - protected val alias = "FNC" - - /** - * Helper function to use in the actual DB function class - * - * @return the SELECT part of the function call SQL query - */ - protected def selectEntry: String = { - val fieldsSeq = fieldsToSelect - if (fieldsSeq.isEmpty) { - "*" - } else { - val aliasToUse = if (alias.isEmpty) { - "" - } else { - s"$alias." - } - fieldsToSelect.map(aliasToUse + _).mkString(",") - } - } } private[slick] trait SlickFunction[I, R] extends SlickFunctionBase[I, R] { - /** * Generates a `SlickQuery[R]` representing the SQL query for the function. * @@ -85,7 +59,6 @@ private[slick] trait SlickFunction[I, R] extends SlickFunctionBase[I, R] { } private[slick] trait SlickFunctionWithStatus[I, R] extends SlickFunctionBase[I, R] { - /** * Generates a `SlickQueryWithStatus[R]` representing the SQL query for the function with status support. * @@ -100,7 +73,6 @@ private[slick] trait SlickFunctionWithStatus[I, R] extends SlickFunctionBase[I, } object SlickFunction { - /** * Class for Slick DB functions with status support. */ From e4a16f7b0bc0526d16edf29dc065778c1f746cf5 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sat, 9 Dec 2023 21:23:53 +0100 Subject: [PATCH 36/90] package renamed, new test for all dates and times common in java, package object with meta instance for zoneddatetime which is not by default available --- doobie/src/it/database/dates_times.sql | 58 +++++++++++++++ doobie/src/it/database/get_all_data_types.sql | 45 ++++++++++++ .../absa/fadb/doobiedb/DatesTimesTest.scala | 71 +++++++++++++++++++ .../doobiedb/DoobieAllDataTypesTest.scala | 62 ++++++++++++++++ .../DoobieMultipleResultFunctionTest.scala | 4 +- .../DoobieOptionalResultFunctionTest.scala | 4 +- .../DoobieSingleResultFunctionTest.scala | 4 +- ...ieSingleResultFunctionWithStatusTest.scala | 4 +- .../{doobie => doobiedb}/DoobieTest.scala | 5 +- .../{doobie => doobiedb}/DoobieEngine.scala | 2 +- .../{doobie => doobiedb}/DoobieFunction.scala | 6 +- .../{doobie => doobiedb}/DoobieQuery.scala | 2 +- .../{doobie => doobiedb}/StatusWithData.scala | 2 +- .../za/co/absa/fadb/doobiedb/package.scala | 22 ++++++ 14 files changed, 275 insertions(+), 16 deletions(-) create mode 100644 doobie/src/it/database/dates_times.sql create mode 100644 doobie/src/it/database/get_all_data_types.sql create mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala create mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieAllDataTypesTest.scala rename doobie/src/it/scala/za/co/absa/fadb/{doobie => doobiedb}/DoobieMultipleResultFunctionTest.scala (93%) rename doobie/src/it/scala/za/co/absa/fadb/{doobie => doobiedb}/DoobieOptionalResultFunctionTest.scala (93%) rename doobie/src/it/scala/za/co/absa/fadb/{doobie => doobiedb}/DoobieSingleResultFunctionTest.scala (94%) rename doobie/src/it/scala/za/co/absa/fadb/{doobie => doobiedb}/DoobieSingleResultFunctionWithStatusTest.scala (93%) rename doobie/src/it/scala/za/co/absa/fadb/{doobie => doobiedb}/DoobieTest.scala (93%) rename doobie/src/main/scala/za/co/absa/fadb/{doobie => doobiedb}/DoobieEngine.scala (98%) rename doobie/src/main/scala/za/co/absa/fadb/{doobie => doobiedb}/DoobieFunction.scala (97%) rename doobie/src/main/scala/za/co/absa/fadb/{doobie => doobiedb}/DoobieQuery.scala (98%) rename doobie/src/main/scala/za/co/absa/fadb/{doobie => doobiedb}/StatusWithData.scala (79%) create mode 100644 doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala diff --git a/doobie/src/it/database/dates_times.sql b/doobie/src/it/database/dates_times.sql new file mode 100644 index 00000000..5c41525b --- /dev/null +++ b/doobie/src/it/database/dates_times.sql @@ -0,0 +1,58 @@ +CREATE TABLE runs.date_time_types ( + offset_date_time TIMESTAMPTZ, + instant TIMESTAMPTZ, + zoned_date_time TIMESTAMPTZ, + local_date_time TIMESTAMP, + local_date DATE, + local_time TIME, + sql_date DATE, + sql_time TIME, + sql_timestamp TIMESTAMP, + util_date TIMESTAMP +); + +INSERT INTO runs.date_time_types ( + offset_date_time, instant, zoned_date_time, local_date_time, local_date, local_time, + sql_date, sql_time, sql_timestamp, util_date +) VALUES ( + '2004-10-19 10:23:54+02', + '2004-10-19 10:23:54+02', + '2004-10-19 10:23:54+02', + '2004-10-19 10:23:54', + '2004-10-19', + '10:23:54', + '2004-10-19', + '10:23:54', + '2004-10-19 10:23:54', + '2004-10-19 10:23:54' +); + + +CREATE OR REPLACE FUNCTION runs.get_all_date_time_types(p_id INT) + RETURNS TABLE( + offset_date_time TIMESTAMPTZ, + instant TIMESTAMPTZ, + zoned_date_time TIMESTAMPTZ, + local_date_time TIMESTAMP, + local_date DATE, + local_time TIME, + sql_date DATE, + sql_time TIME, + sql_timestamp TIMESTAMP, + util_date TIMESTAMP + ) AS $$ +BEGIN + RETURN QUERY SELECT + T.offset_date_time, + T.instant, + T.zoned_date_time, + T.local_date_time, + T.local_date, + T.local_time, + T.sql_date, + T.sql_time, + T.sql_timestamp, + T.util_date + FROM runs.date_time_types T limit p_id; +END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/doobie/src/it/database/get_all_data_types.sql b/doobie/src/it/database/get_all_data_types.sql new file mode 100644 index 00000000..4556e607 --- /dev/null +++ b/doobie/src/it/database/get_all_data_types.sql @@ -0,0 +1,45 @@ +CREATE TABLE runs.all_data_types ( + col_smallint SMALLINT, + col_integer INTEGER, + col_bigint BIGINT, + col_decimal DECIMAL(5, 2), + col_numeric NUMERIC(10, 5), + col_real REAL, + col_double_precision DOUBLE PRECISION, + col_money MONEY, + col_char CHAR(10), + col_varchar VARCHAR(50), + col_text TEXT, + col_bytea BYTEA, + col_timestamp TIMESTAMP, + col_date DATE, + col_time TIME, + col_boolean BOOLEAN, + col_bit BIT(4), + col_uuid UUID, + col_json JSON, + col_jsonb JSONB, + col_int_array INTEGER[], + col_text_array TEXT[] +); + +INSERT INTO runs.all_data_types ( + col_smallint, col_integer, col_bigint, col_decimal, col_numeric, col_real, col_double_precision, + col_money, col_char, col_varchar, col_text, col_timestamp, col_date, col_time, col_boolean, + col_bit, col_uuid, col_json, col_jsonb, col_int_array, col_text_array +) VALUES ( + 1, 2, 3, 4.5, 6.789, 7.8, 9.01, + 100.00, 'char', 'varchar', 'text', CURRENT_TIMESTAMP, CURRENT_DATE, CURRENT_TIME, true, + B'1010', 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', '{"key": "value"}', '{"key": "value"}', + ARRAY[1,2,3], ARRAY['text1', 'text2', 'text3'] + ); + +CREATE OR REPLACE FUNCTION runs.get_all_data_types(p_col_integer INTEGER) +RETURNS SETOF runs.all_data_types +AS +$$ +BEGIN + RETURN QUERY SELECT * FROM runs.all_data_types WHERE col_integer = p_col_integer; +END; +$$ +LANGUAGE plpgsql; \ No newline at end of file diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala new file mode 100644 index 00000000..34cd8623 --- /dev/null +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala @@ -0,0 +1,71 @@ +package za.co.absa.fadb.doobiedb + +import cats.effect.IO +import cats.effect.unsafe.implicits.global +import doobie.implicits.toSqlInterpolator +import doobie.util.Read +import doobie.util.fragment.Fragment +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieSingleResultFunction + +class DatesTimesTest extends AnyFunSuite with DoobieTest { + + // these imports are needed + import doobie.postgres.implicits._ + import doobie.implicits.javasql._ + + // this import is needed for the implicit Meta[java.time.ZonedDateTime] + import za.co.absa.fadb.doobiedb.implicits.zonedDateTimeMeta + + case class DatesTimes( + offsetDateTime: java.time.OffsetDateTime, + instant: java.time.Instant, + zonedDateTime: java.time.ZonedDateTime, + localDateTime: java.time.LocalDateTime, + localDate: java.time.LocalDate, + localTime: java.time.LocalTime, + sqlDate: java.sql.Date, + sqlTime: java.sql.Time, + sqlTimestamp: java.sql.Timestamp, + utilDate: java.util.Date + ) + + class GetAllDateTimeTypes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) + extends DoobieSingleResultFunction[Int, DatesTimes, IO] { + + override def sql(values: Int)(implicit read: Read[DatesTimes]): Fragment = + sql"SELECT * FROM ${Fragment.const(functionName)}($values)" + } + + private val getAllDateTimeTypes = new GetAllDateTimeTypes()(Runs, new DoobieEngine(transactor)) + + test("DoobieTest") { + val offsetDateTime = java.time.OffsetDateTime.parse("2004-10-19T08:23:54Z") + val instant = java.time.Instant.parse("2004-10-19T08:23:54Z") + val zonedDateTime = java.time.ZonedDateTime.parse("2004-10-19T10:23:54+02:00[Europe/Prague]") + val localDateTime = java.time.LocalDateTime.parse("2004-10-19T10:23:54") + val localDate = java.time.LocalDate.parse("2004-10-19") + val localTime = java.time.LocalTime.parse("10:23:54") + val sqlDate = java.sql.Date.valueOf("2004-10-19") + val sqlTime = java.sql.Time.valueOf("10:23:54") + val sqlTimestamp = java.sql.Timestamp.valueOf("2004-10-19 10:23:54.0") + val utilDate = new java.util.Date(sqlDate.getTime) + + val expectedDatesTimes = DatesTimes( + offsetDateTime, + instant, + zonedDateTime, + localDateTime, + localDate, + localTime, + sqlDate, + sqlTime, + sqlTimestamp, + utilDate + ) + val result = getAllDateTimeTypes(1).unsafeRunSync() + assert(expectedDatesTimes == result) + } + +} diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieAllDataTypesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieAllDataTypesTest.scala new file mode 100644 index 00000000..b86abd12 --- /dev/null +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieAllDataTypesTest.scala @@ -0,0 +1,62 @@ +//package za.co.absa.fadb.doobie +// +//import cats.effect.IO +//import cats.effect.unsafe.implicits.global +//import doobie.implicits.toSqlInterpolator +//import doobie.util.Read +//import doobie.util.fragment.Fragment +//import org.scalatest.funsuite.AnyFunSuite +//import za.co.absa.fadb.DBSchema +//import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunction +// +//import java.sql.{Date, Time, Timestamp} +//import java.util.UUID +// +//class DoobieAllDataTypesTest extends AnyFunSuite with DoobieTest { +// +// import doobie.postgres._ +// import doobie.postgres.implicits._ +// +// // https://tpolecat.github.io/doobie/docs/15-Extensions-PostgreSQL.html +// // https://tpolecat.github.io/doobie/docs/17-FAQ.html#how-do-i-use-java-time-types-with-doobie- +// +// case class AllDataTypes( +// colSmallint: Option[Short], +// colInteger: Option[Int], +// colBigint: Option[Long], +// colDecimal: Option[BigDecimal], +// colNumeric: Option[BigDecimal], +// colReal: Option[Float], +// colDoublePrecision: Option[Double], +// colMoney: Option[Double], +// colChar: Option[String], +// colVarchar: Option[String], +// colText: Option[String], +// colTimestamp: Option[Timestamp], +// colDate: Option[Date], +// colTime: Option[Time], +// colBoolean: Option[Boolean], +// colUuid: Option[UUID], +// colJson: Option[String], +// colJsonb: Option[String], +// colIntArray: Option[Array[Int]], +// colTextArray: Option[Array[String]] +// ) +// +// implicit val readAllDataTypes: Read[AllDataTypes] = Read[AllDataTypes] +// +// class GetAllDataTypes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) +// extends DoobieSingleResultFunction[Int, AllDataTypes, IO] { +// +// override def sql(values: Int)(implicit read: Read[AllDataTypes]): Fragment = +// sql"SELECT * FROM ${Fragment.const(functionName)}($values)" +// } +// +// private val getAllDataTypes = new GetAllDataTypes()(Runs, new DoobieEngine(transactor)) +// +// test("DoobieTest") { +// val result = getAllDataTypes(2).unsafeRunSync() +// println(result) +// } +// +//} diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieMultipleResultFunctionTest.scala similarity index 93% rename from doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieMultipleResultFunctionTest.scala index f0323196..5851bc3c 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieMultipleResultFunctionTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobie +package za.co.absa.fadb.doobiedb import cats.effect.IO import cats.effect.unsafe.implicits.global @@ -23,7 +23,7 @@ import doobie.util.Read import doobie.util.fragment.Fragment import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobie.DoobieFunction.DoobieMultipleResultFunction +import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieMultipleResultFunction class DoobieMultipleResultFunctionTest extends AnyFunSuite with DoobieTest { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOptionalResultFunctionTest.scala similarity index 93% rename from doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOptionalResultFunctionTest.scala index 0178b7a5..c115b6f0 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOptionalResultFunctionTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobie +package za.co.absa.fadb.doobiedb import cats.effect.IO import cats.effect.unsafe.implicits.global @@ -23,7 +23,7 @@ import doobie.util.Read import doobie.util.fragment.Fragment import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobie.DoobieFunction.DoobieOptionalResultFunction +import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieOptionalResultFunction class DoobieOptionalResultFunctionTest extends AnyFunSuite with DoobieTest { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionTest.scala similarity index 94% rename from doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionTest.scala index 0e5d80de..7d10b7fd 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobie +package za.co.absa.fadb.doobiedb import cats.effect.IO import cats.effect.unsafe.implicits.global @@ -23,7 +23,7 @@ import doobie.util.Read import doobie.util.fragment.Fragment import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunction +import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieSingleResultFunction class DoobieSingleResultFunctionTest extends AnyFunSuite with DoobieTest { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala similarity index 93% rename from doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala index 1a80de03..b627280a 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobie +package za.co.absa.fadb.doobiedb import cats.effect.IO import cats.effect.unsafe.implicits.global @@ -23,7 +23,7 @@ import doobie.implicits.toSqlInterpolator import doobie.util.Read import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStatus +import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieSingleResultFunctionWithStatus import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling class DoobieSingleResultFunctionWithStatusTest extends AnyFunSuite with DoobieTest { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala similarity index 93% rename from doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala index 625b08b9..4ba6cc03 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobie +package za.co.absa.fadb.doobiedb import cats.effect.IO import doobie.util.log.{LogEvent, LogHandler} @@ -37,7 +37,8 @@ trait DoobieTest { } protected val transactor = Transactor.fromDriverManager[IO]( - "org.postgresql.ds.PGSimpleDataSource", +// "org.postgresql.ds.PGSimpleDataSource", + "org.postgresql.Driver", "jdbc:postgresql://localhost:5432/movies", "postgres", "postgres", diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala similarity index 98% rename from doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala rename to doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala index 2551bfd9..fbc6b57c 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobie +package za.co.absa.fadb.doobiedb import cats.Monad import cats.effect.Async diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala similarity index 97% rename from doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala rename to doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala index 5b956768..1cd366d5 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobie +package za.co.absa.fadb.doobiedb import cats.Monad import cats.effect.kernel.Async @@ -39,7 +39,7 @@ trait DoobieFunctionBase[R] { * @tparam I the input type of the function * @tparam R the result type of the function */ -private[doobie] trait DoobieFunction[I, R] extends DoobieFunctionBase[R] { +private[doobiedb] trait DoobieFunction[I, R] extends DoobieFunctionBase[R] { /** * Generates a Doobie `Fragment` representing the SQL query for the function. * @@ -57,7 +57,7 @@ private[doobie] trait DoobieFunction[I, R] extends DoobieFunctionBase[R] { protected def query(values: I): DoobieQuery[R] = new DoobieQuery[R](sql(values)) } -private[doobie] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBase[R] { +private[doobiedb] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBase[R] { /** * The `Read[StatusWithData[R]]` instance used to read the query result with status into `StatusWithData[R]`. */ diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieQuery.scala similarity index 98% rename from doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala rename to doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieQuery.scala index c6f868f9..7f632c70 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieQuery.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobie +package za.co.absa.fadb.doobiedb import doobie.util.Read import doobie.util.fragment.Fragment diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala similarity index 79% rename from doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala rename to doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala index ce4e0da5..d90db69a 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala @@ -1,4 +1,4 @@ -package za.co.absa.fadb.doobie +package za.co.absa.fadb.doobiedb /** * Represents a function status with data. diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala new file mode 100644 index 00000000..c4ca6313 --- /dev/null +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala @@ -0,0 +1,22 @@ +package za.co.absa.fadb + +import doobie.util.meta.Meta +import doobie.implicits.javasql._ + +import java.sql.Timestamp +import java.time.ZoneId + +// can't be named doobie due to a naming conflict +package object doobiedb { + + object implicits { + // Doobie does not provide a `Meta` instance for `ZonedDateTime` out of the box because database support for this type is not universal. + // This `Meta` instance converts between `ZonedDateTime` and `Timestamp`, using the system's default time zone. + // Please note that this might not be the correct behavior for your application if your database stores timestamps in a different time zone. + // Meta[A] is a convenience type class for introducing a symmetric `Get`/`Put` pair into implicit scope, and for deriving new symmetric pairs. + implicit val zonedDateTimeMeta: Meta[java.time.ZonedDateTime] = { + Meta[Timestamp].timap(t => t.toLocalDateTime.atZone(ZoneId.systemDefault()))(zdt => Timestamp.from(zdt.toInstant)) + } + } + +} From 742ab50e8630e44b2843474cc5ecb4d9ff26dc31 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sat, 9 Dec 2023 22:31:56 +0100 Subject: [PATCH 37/90] write times and dates test --- doobie/src/it/database/create_actor.sql | 6 +-- doobie/src/it/database/dates_times.sql | 48 +++++++++++++++++++ .../absa/fadb/doobiedb/DatesTimesTest.scala | 42 +++++++++++++++- 3 files changed, 90 insertions(+), 6 deletions(-) diff --git a/doobie/src/it/database/create_actor.sql b/doobie/src/it/database/create_actor.sql index 215e4c24..4ace32dd 100644 --- a/doobie/src/it/database/create_actor.sql +++ b/doobie/src/it/database/create_actor.sql @@ -29,10 +29,8 @@ INSERT INTO runs.actors(first_name, last_name) VALUES (i_first_name, i_last_name) RETURNING actor_id INTO o_actor_id; -status -:= 11; - status_text -:= 'Actor created'; + status:= 11; + status_text:= 'Actor created'; RETURN; END; diff --git a/doobie/src/it/database/dates_times.sql b/doobie/src/it/database/dates_times.sql index 5c41525b..b17c334b 100644 --- a/doobie/src/it/database/dates_times.sql +++ b/doobie/src/it/database/dates_times.sql @@ -1,4 +1,5 @@ CREATE TABLE runs.date_time_types ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, offset_date_time TIMESTAMPTZ, instant TIMESTAMPTZ, zoned_date_time TIMESTAMPTZ, @@ -55,4 +56,51 @@ BEGIN T.util_date FROM runs.date_time_types T limit p_id; END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION runs.insert_dates_times( + IN p_offset_date_time TIMESTAMPTZ, + IN p_instant TIMESTAMPTZ, + IN p_zoned_date_time TIMESTAMPTZ, + IN p_local_date_time TIMESTAMP, + IN p_local_date DATE, + IN p_local_time TIME, + IN p_sql_date DATE, + IN p_sql_time TIME, + IN p_sql_timestamp TIMESTAMP, + IN p_util_date DATE, + OUT status INTEGER, + OUT status_text TEXT, + OUT o_id INTEGER +) RETURNS record AS $$ +BEGIN + INSERT INTO runs.date_time_types ( + offset_date_time, + instant, + zoned_date_time, + local_date_time, + local_date, + local_time, + sql_date, + sql_time, + sql_timestamp, + util_date + ) VALUES ( + p_offset_date_time, + p_instant, + p_zoned_date_time, + p_local_date_time, + p_local_date, + p_local_time, + p_sql_date, + p_sql_time, + p_sql_timestamp, + p_util_date + ) RETURNING id INTO o_id; + + status := 11; + status_text := 'OK'; + + RETURN; +END; $$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala index 34cd8623..631a8e3f 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala @@ -7,7 +7,8 @@ import doobie.util.Read import doobie.util.fragment.Fragment import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieSingleResultFunction +import za.co.absa.fadb.doobiedb.DoobieFunction.{DoobieSingleResultFunction, DoobieSingleResultFunctionWithStatus} +import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling class DatesTimesTest extends AnyFunSuite with DoobieTest { @@ -38,9 +39,17 @@ class DatesTimesTest extends AnyFunSuite with DoobieTest { sql"SELECT * FROM ${Fragment.const(functionName)}($values)" } + class InsertDatesTimes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) + extends DoobieSingleResultFunctionWithStatus[DatesTimes, Int, IO] with StandardStatusHandling { + + override def sql(values: DatesTimes)(implicit read: Read[StatusWithData[Int]]): Fragment = + sql"SELECT * FROM ${Fragment.const(functionName)}(${values.offsetDateTime}, ${values.instant}, ${values.zonedDateTime}, ${values.localDateTime}, ${values.localDate}, ${values.localTime}, ${values.sqlDate}, ${values.sqlTime}, ${values.sqlTimestamp}, ${values.utilDate})" + } + private val getAllDateTimeTypes = new GetAllDateTimeTypes()(Runs, new DoobieEngine(transactor)) + private val insertDatesTimes = new InsertDatesTimes()(Runs, new DoobieEngine(transactor)) - test("DoobieTest") { + test("DoobieTest READ") { val offsetDateTime = java.time.OffsetDateTime.parse("2004-10-19T08:23:54Z") val instant = java.time.Instant.parse("2004-10-19T08:23:54Z") val zonedDateTime = java.time.ZonedDateTime.parse("2004-10-19T10:23:54+02:00[Europe/Prague]") @@ -68,4 +77,33 @@ class DatesTimesTest extends AnyFunSuite with DoobieTest { assert(expectedDatesTimes == result) } + test("DoobieTest WRITE") { + val offsetDateTime = java.time.OffsetDateTime.parse("2004-10-19T08:23:54Z") + val instant = java.time.Instant.parse("2004-10-19T08:23:54Z") + val zonedDateTime = java.time.ZonedDateTime.parse("2004-10-19T10:23:54+02:00[Europe/Prague]") + val localDateTime = java.time.LocalDateTime.parse("2004-10-19T10:23:54") + val localDate = java.time.LocalDate.parse("2004-10-19") + val localTime = java.time.LocalTime.parse("10:23:54") + val sqlDate = java.sql.Date.valueOf("2004-10-19") + val sqlTime = java.sql.Time.valueOf("10:23:54") + val sqlTimestamp = java.sql.Timestamp.valueOf("2004-10-19 10:23:54.0") + val utilDate = new java.util.Date(sqlDate.getTime) + + val datesTimes = DatesTimes( + offsetDateTime, + instant, + zonedDateTime, + localDateTime, + localDate, + localTime, + sqlDate, + sqlTime, + sqlTimestamp, + utilDate + ) + + val result = insertDatesTimes(datesTimes).unsafeRunSync() + assert(result.isRight) + } + } From 80a72d453bf9c485233aaf822660c7a1d95101d3 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sun, 10 Dec 2023 11:24:46 +0100 Subject: [PATCH 38/90] test of unsuccessfull functions with status --- doobie/src/it/database/error_if_not_one.sql | 14 +++++ ...ieSingleResultFunctionWithStatusTest.scala | 56 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 doobie/src/it/database/error_if_not_one.sql diff --git a/doobie/src/it/database/error_if_not_one.sql b/doobie/src/it/database/error_if_not_one.sql new file mode 100644 index 00000000..24be9e19 --- /dev/null +++ b/doobie/src/it/database/error_if_not_one.sql @@ -0,0 +1,14 @@ +CREATE OR REPLACE FUNCTION runs.error_if_not_one(p_input INT) +RETURNS TABLE( + status INT, + status_text TEXT, + input_value INT +) AS $$ +BEGIN + IF p_input != 1 THEN + RETURN QUERY SELECT 99 AS status, 'error' AS status_text, NULL::INT AS input_value; + ELSE + RETURN QUERY SELECT 11 AS status, 'success' AS status_text, p_input AS input_value; + END IF; +END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala index b627280a..acc1853d 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala @@ -21,6 +21,7 @@ import cats.effect.unsafe.implicits.global import doobie.Fragment import doobie.implicits.toSqlInterpolator import doobie.util.Read +import doobie.util.invariant.NonNullableColumnRead import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieSingleResultFunctionWithStatus @@ -37,7 +38,16 @@ class DoobieSingleResultFunctionWithStatusTest extends AnyFunSuite with DoobieTe } } + class ErrorIfNotOne(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) + extends DoobieSingleResultFunctionWithStatus[Int, Int, IO] + with StandardStatusHandling { + + override def sql(values: Int)(implicit read: Read[StatusWithData[Int]]): Fragment = + sql"SELECT * FROM ${Fragment.const(functionName)}(${values})" + } + private val createActor = new CreateActor()(Runs, new DoobieEngine(transactor)) + private val errorIfNotOne = new ErrorIfNotOne()(Runs, new DoobieEngine(transactor)) test("DoobieTest with status handling") { val requestBody = CreateActorRequestBody("Pavel", "Marek") @@ -45,4 +55,50 @@ class DoobieSingleResultFunctionWithStatusTest extends AnyFunSuite with DoobieTe assert(result.isRight) println(result) } + + test("error if not one with status handling") { + val result = errorIfNotOne(1).unsafeRunSync() + assert(result.isRight) + println(result) + } + + test("error if not one with status handling - error") { + // throws null exception + // SQL `NULL` read at column 3 (JDBC type Integer) but mapping is to a non-Option type; use Option here. Note that JDBC column indexing is 1-based. + assertThrows[NonNullableColumnRead](errorIfNotOne(2).unsafeRunSync()) + } + + test("error if not one with status handling - error - null wrapped in option") { + class ErrorIfNotOne(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) + extends DoobieSingleResultFunctionWithStatus[Int, Option[Int], IO] + with StandardStatusHandling { + + override def sql(values: Int)(implicit read: Read[StatusWithData[Option[Int]]]): Fragment = + sql"SELECT * FROM ${Fragment.const(functionName)}(${values})" + } + + val errorIfNotOne = new ErrorIfNotOne()(Runs, new DoobieEngine(transactor)) + + // does not throw because null is wrapped in option + val result = errorIfNotOne(2).unsafeRunSync() + assert(result.isLeft) + } + + test("error if not one with status handling - success - null wrapped in option") { + class ErrorIfNotOne(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) + extends DoobieSingleResultFunctionWithStatus[Int, Option[Int], IO] + with StandardStatusHandling { + + override def sql(values: Int)(implicit read: Read[StatusWithData[Option[Int]]]): Fragment = + sql"SELECT * FROM ${Fragment.const(functionName)}(${values})" + } + + val errorIfNotOne = new ErrorIfNotOne()(Runs, new DoobieEngine(transactor)) + + val result = errorIfNotOne(1).unsafeRunSync() + result match { + case Left(_) => fail("should not be left") + case Right(value) => assert(value.contains(1)) + } + } } From af15e7f0b6ffe15db70c4b2561d21d87a71ec383 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sun, 10 Dec 2023 21:01:25 +0100 Subject: [PATCH 39/90] other types reading test --- doobie/src/it/database/other_types.sql | 90 +++++++++++++++++++ .../doobiedb/DoobieAllDataTypesTest.scala | 62 ------------- .../fadb/doobiedb/DoobieOtherTypesTest.scala | 48 ++++++++++ 3 files changed, 138 insertions(+), 62 deletions(-) create mode 100644 doobie/src/it/database/other_types.sql delete mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieAllDataTypesTest.scala create mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala diff --git a/doobie/src/it/database/other_types.sql b/doobie/src/it/database/other_types.sql new file mode 100644 index 00000000..a0eb2104 --- /dev/null +++ b/doobie/src/it/database/other_types.sql @@ -0,0 +1,90 @@ +CREATE TYPE my_enum AS ENUM ('enum1', 'enum2', 'enum3'); + +CREATE EXTENSION IF NOT EXISTS "ltree"; +CREATE EXTENSION IF NOT EXISTS "hstore"; +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +drop table runs.other_types cascade; + +CREATE TABLE runs.other_types ( + id INT PRIMARY KEY, + ltree_col LTREE, + inet_col INET, + macaddr_col MACADDR, + hstore_col HSTORE, + cidr_col CIDR, + json_col JSON, + jsonb_col JSONB, + uuid_col UUID, + array_col INT[] +); + +INSERT INTO runs.other_types VALUES ( + 1, + 'Top.Science.Astronomy', + '192.168.1.1', + '08:00:2b:01:02:03', + 'key=>value', + '192.168.1/24', + '{"key": "value"}', + '{"key": "value"}', + uuid_generate_v4(), + ARRAY[1,2,3] +); + +CREATE OR REPLACE FUNCTION runs.read_other_types(p_id INT) +RETURNS TABLE( + id INT, + ltree_col LTREE, + inet_col INET, + macaddr_col MACADDR, + hstore_col HSTORE, + cidr_col CIDR, + json_col JSON, + jsonb_col JSONB, + uuid_col UUID, + array_col INT[] +) AS $$ +BEGIN + RETURN QUERY SELECT * FROM runs.other_types T WHERE T.id = p_id; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION runs.insert_other_types( + p_id INT, + p_ltree_col LTREE, + p_inet_col INET, + p_macaddr_col MACADDR, + p_hstore_col HSTORE, + p_cidr_col CIDR, + p_json_col JSON, + p_jsonb_col JSONB, + p_uuid_col UUID, + p_array_col INT[] +) RETURNS TABLE( + status INT, + status_text TEXT +) AS $$ +BEGIN + BEGIN + INSERT INTO runs.other_types VALUES ( + p_id, + p_ltree_col, + p_inet_col, + p_macaddr_col, + p_hstore_col, + p_cidr_col, + p_json_col, + p_jsonb_col, + p_uuid_col, + p_array_col + ); + status := 11; + status_text := 'ok'; + EXCEPTION WHEN unique_violation THEN + status := 31; + status_text := 'data conflict'; + END; + RETURN NEXT; +END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieAllDataTypesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieAllDataTypesTest.scala deleted file mode 100644 index b86abd12..00000000 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieAllDataTypesTest.scala +++ /dev/null @@ -1,62 +0,0 @@ -//package za.co.absa.fadb.doobie -// -//import cats.effect.IO -//import cats.effect.unsafe.implicits.global -//import doobie.implicits.toSqlInterpolator -//import doobie.util.Read -//import doobie.util.fragment.Fragment -//import org.scalatest.funsuite.AnyFunSuite -//import za.co.absa.fadb.DBSchema -//import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunction -// -//import java.sql.{Date, Time, Timestamp} -//import java.util.UUID -// -//class DoobieAllDataTypesTest extends AnyFunSuite with DoobieTest { -// -// import doobie.postgres._ -// import doobie.postgres.implicits._ -// -// // https://tpolecat.github.io/doobie/docs/15-Extensions-PostgreSQL.html -// // https://tpolecat.github.io/doobie/docs/17-FAQ.html#how-do-i-use-java-time-types-with-doobie- -// -// case class AllDataTypes( -// colSmallint: Option[Short], -// colInteger: Option[Int], -// colBigint: Option[Long], -// colDecimal: Option[BigDecimal], -// colNumeric: Option[BigDecimal], -// colReal: Option[Float], -// colDoublePrecision: Option[Double], -// colMoney: Option[Double], -// colChar: Option[String], -// colVarchar: Option[String], -// colText: Option[String], -// colTimestamp: Option[Timestamp], -// colDate: Option[Date], -// colTime: Option[Time], -// colBoolean: Option[Boolean], -// colUuid: Option[UUID], -// colJson: Option[String], -// colJsonb: Option[String], -// colIntArray: Option[Array[Int]], -// colTextArray: Option[Array[String]] -// ) -// -// implicit val readAllDataTypes: Read[AllDataTypes] = Read[AllDataTypes] -// -// class GetAllDataTypes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) -// extends DoobieSingleResultFunction[Int, AllDataTypes, IO] { -// -// override def sql(values: Int)(implicit read: Read[AllDataTypes]): Fragment = -// sql"SELECT * FROM ${Fragment.const(functionName)}($values)" -// } -// -// private val getAllDataTypes = new GetAllDataTypes()(Runs, new DoobieEngine(transactor)) -// -// test("DoobieTest") { -// val result = getAllDataTypes(2).unsafeRunSync() -// println(result) -// } -// -//} diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala new file mode 100644 index 00000000..d391bde3 --- /dev/null +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala @@ -0,0 +1,48 @@ +package za.co.absa.fadb.doobiedb + +import cats.effect.IO +import cats.effect.unsafe.implicits.global +import doobie.implicits.toSqlInterpolator +import doobie.util.Read +import doobie.util.fragment.Fragment +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieSingleResultFunction + +import java.net.InetAddress +import java.util.UUID + + +class DoobieOtherTypesTest extends AnyFunSuite with DoobieTest { + + import doobie.postgres.implicits._ + + case class OtherTypesData( + id: Int, + ltreeCol: String, + inetCol: InetAddress, + macaddrCol: String, + hstoreCol: Map[String, String], + cidrCol: String, + jsonCol: String, + jsonbCol: String, + uuidCol: UUID, + arrayCol: Array[Int] + ) + + + class ReadOtherTypes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) + extends DoobieSingleResultFunction[Int, OtherTypesData, IO] { + + override def sql(values: Int)(implicit read: Read[OtherTypesData]): Fragment = + sql"SELECT * FROM ${Fragment.const(functionName)}($values)" + } + + private val readOtherTypes = new ReadOtherTypes()(Runs, new DoobieEngine(transactor)) + + test("DoobieTest") { + val result = readOtherTypes(1).unsafeRunSync() + println(result) + } + +} From 6711e886284a8be03f1107679a255992856a4be5 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Mon, 11 Dec 2023 10:12:44 +0100 Subject: [PATCH 40/90] clean-up --- doobie/src/it/database/actors.ddl | 3 +- doobie/src/it/database/create_actor.sql | 9 ++- doobie/src/it/database/get_actor_by_id.sql | 3 +- doobie/src/it/database/get_actors.sql | 3 +- doobie/src/it/database/get_all_data_types.sql | 45 ------------ doobie/src/it/database/other_types.sql | 14 ++-- .../absa/fadb/doobiedb/DatesTimesTest.scala | 21 ++++-- .../DoobieMultipleResultFunctionTest.scala | 2 +- .../DoobieOptionalResultFunctionTest.scala | 2 +- .../fadb/doobiedb/DoobieOtherTypesTest.scala | 70 ++++++++++++++++++- .../DoobieSingleResultFunctionTest.scala | 2 +- ...ieSingleResultFunctionWithStatusTest.scala | 42 ++++------- .../za/co/absa/fadb/doobiedb/DoobieTest.scala | 1 - .../za/co/absa/fadb/doobiedb/package.scala | 18 +++-- .../SlickMultipleResultFunctionTest.scala | 2 +- .../SlickOptionalResultFunctionTest.scala | 2 +- ...ckSingleResultFunctionWithStatusTest.scala | 2 +- .../za/co/absa/fadb/slick/SlickQuery.scala | 15 ++++ 18 files changed, 143 insertions(+), 113 deletions(-) delete mode 100644 doobie/src/it/database/get_all_data_types.sql diff --git a/doobie/src/it/database/actors.ddl b/doobie/src/it/database/actors.ddl index 7130616c..3ffdc868 100644 --- a/doobie/src/it/database/actors.ddl +++ b/doobie/src/it/database/actors.ddl @@ -1,4 +1,5 @@ CREATE TABLE IF NOT EXISTS runs.actors( actor_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, first_name VARCHAR(150) NOT NULL, - last_name VARCHAR(150) NOT NULL); \ No newline at end of file + last_name VARCHAR(150) NOT NULL +); \ No newline at end of file diff --git a/doobie/src/it/database/create_actor.sql b/doobie/src/it/database/create_actor.sql index 4ace32dd..d8e0f098 100644 --- a/doobie/src/it/database/create_actor.sql +++ b/doobie/src/it/database/create_actor.sql @@ -15,8 +15,7 @@ -- Example: -- SELECT * FROM runs.create_actor('John', 'Doe'); -CREATE -OR REPLACE FUNCTION runs.create_actor( +CREATE OR REPLACE FUNCTION runs.create_actor( IN i_first_name TEXT, IN i_last_name TEXT, OUT status INTEGER, @@ -25,9 +24,9 @@ OR REPLACE FUNCTION runs.create_actor( ) RETURNS record AS $$ BEGIN -INSERT INTO runs.actors(first_name, last_name) -VALUES (i_first_name, i_last_name) RETURNING actor_id -INTO o_actor_id; + INSERT INTO runs.actors(first_name, last_name) + VALUES (i_first_name, i_last_name) RETURNING actor_id + INTO o_actor_id; status:= 11; status_text:= 'Actor created'; diff --git a/doobie/src/it/database/get_actor_by_id.sql b/doobie/src/it/database/get_actor_by_id.sql index e6e64df9..8a8039de 100644 --- a/doobie/src/it/database/get_actor_by_id.sql +++ b/doobie/src/it/database/get_actor_by_id.sql @@ -18,8 +18,7 @@ * * This will return the actor with ID 1, if he/she exists. */ -CREATE -OR REPLACE FUNCTION runs.get_actor_by_id( +CREATE OR REPLACE FUNCTION runs.get_actor_by_id( i_actor_id INTEGER ) RETURNS TABLE ( actor_id INTEGER, diff --git a/doobie/src/it/database/get_actors.sql b/doobie/src/it/database/get_actors.sql index 46b9bfaf..cc465d03 100644 --- a/doobie/src/it/database/get_actors.sql +++ b/doobie/src/it/database/get_actors.sql @@ -1,5 +1,4 @@ -CREATE -OR REPLACE FUNCTION runs.get_actors( +CREATE OR REPLACE FUNCTION runs.get_actors( i_first_name TEXT, i_last_name TEXT ) RETURNS TABLE ( diff --git a/doobie/src/it/database/get_all_data_types.sql b/doobie/src/it/database/get_all_data_types.sql deleted file mode 100644 index 4556e607..00000000 --- a/doobie/src/it/database/get_all_data_types.sql +++ /dev/null @@ -1,45 +0,0 @@ -CREATE TABLE runs.all_data_types ( - col_smallint SMALLINT, - col_integer INTEGER, - col_bigint BIGINT, - col_decimal DECIMAL(5, 2), - col_numeric NUMERIC(10, 5), - col_real REAL, - col_double_precision DOUBLE PRECISION, - col_money MONEY, - col_char CHAR(10), - col_varchar VARCHAR(50), - col_text TEXT, - col_bytea BYTEA, - col_timestamp TIMESTAMP, - col_date DATE, - col_time TIME, - col_boolean BOOLEAN, - col_bit BIT(4), - col_uuid UUID, - col_json JSON, - col_jsonb JSONB, - col_int_array INTEGER[], - col_text_array TEXT[] -); - -INSERT INTO runs.all_data_types ( - col_smallint, col_integer, col_bigint, col_decimal, col_numeric, col_real, col_double_precision, - col_money, col_char, col_varchar, col_text, col_timestamp, col_date, col_time, col_boolean, - col_bit, col_uuid, col_json, col_jsonb, col_int_array, col_text_array -) VALUES ( - 1, 2, 3, 4.5, 6.789, 7.8, 9.01, - 100.00, 'char', 'varchar', 'text', CURRENT_TIMESTAMP, CURRENT_DATE, CURRENT_TIME, true, - B'1010', 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', '{"key": "value"}', '{"key": "value"}', - ARRAY[1,2,3], ARRAY['text1', 'text2', 'text3'] - ); - -CREATE OR REPLACE FUNCTION runs.get_all_data_types(p_col_integer INTEGER) -RETURNS SETOF runs.all_data_types -AS -$$ -BEGIN - RETURN QUERY SELECT * FROM runs.all_data_types WHERE col_integer = p_col_integer; -END; -$$ -LANGUAGE plpgsql; \ No newline at end of file diff --git a/doobie/src/it/database/other_types.sql b/doobie/src/it/database/other_types.sql index a0eb2104..a0bd88b9 100644 --- a/doobie/src/it/database/other_types.sql +++ b/doobie/src/it/database/other_types.sql @@ -1,11 +1,3 @@ -CREATE TYPE my_enum AS ENUM ('enum1', 'enum2', 'enum3'); - -CREATE EXTENSION IF NOT EXISTS "ltree"; -CREATE EXTENSION IF NOT EXISTS "hstore"; -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - -drop table runs.other_types cascade; - CREATE TABLE runs.other_types ( id INT PRIMARY KEY, ltree_col LTREE, @@ -32,7 +24,7 @@ INSERT INTO runs.other_types VALUES ( ARRAY[1,2,3] ); -CREATE OR REPLACE FUNCTION runs.read_other_types(p_id INT) +CREATE OR REPLACE FUNCTION runs.read_other_types(p_id INT) RETURNS TABLE( id INT, ltree_col LTREE, @@ -63,7 +55,8 @@ CREATE OR REPLACE FUNCTION runs.insert_other_types( p_array_col INT[] ) RETURNS TABLE( status INT, - status_text TEXT + status_text TEXT, + o_id INT ) AS $$ BEGIN BEGIN @@ -81,6 +74,7 @@ BEGIN ); status := 11; status_text := 'ok'; + o_id := p_id; EXCEPTION WHEN unique_violation THEN status := 31; status_text := 'data conflict'; diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala index 631a8e3f..58172924 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala @@ -17,7 +17,7 @@ class DatesTimesTest extends AnyFunSuite with DoobieTest { import doobie.implicits.javasql._ // this import is needed for the implicit Meta[java.time.ZonedDateTime] - import za.co.absa.fadb.doobiedb.implicits.zonedDateTimeMeta + import za.co.absa.fadb.doobiedb.postgres.implicits.zonedDateTimeMeta case class DatesTimes( offsetDateTime: java.time.OffsetDateTime, @@ -43,13 +43,26 @@ class DatesTimesTest extends AnyFunSuite with DoobieTest { extends DoobieSingleResultFunctionWithStatus[DatesTimes, Int, IO] with StandardStatusHandling { override def sql(values: DatesTimes)(implicit read: Read[StatusWithData[Int]]): Fragment = - sql"SELECT * FROM ${Fragment.const(functionName)}(${values.offsetDateTime}, ${values.instant}, ${values.zonedDateTime}, ${values.localDateTime}, ${values.localDate}, ${values.localTime}, ${values.sqlDate}, ${values.sqlTime}, ${values.sqlTimestamp}, ${values.utilDate})" + sql""" + SELECT * FROM ${Fragment.const(functionName)}( + ${values.offsetDateTime}, + ${values.instant}, + ${values.zonedDateTime}, + ${values.localDateTime}, + ${values.localDate}, + ${values.localTime}, + ${values.sqlDate}, + ${values.sqlTime}, + ${values.sqlTimestamp}, + ${values.utilDate} + ) + """ } private val getAllDateTimeTypes = new GetAllDateTimeTypes()(Runs, new DoobieEngine(transactor)) private val insertDatesTimes = new InsertDatesTimes()(Runs, new DoobieEngine(transactor)) - test("DoobieTest READ") { + test("Reading different date/time types from the database") { val offsetDateTime = java.time.OffsetDateTime.parse("2004-10-19T08:23:54Z") val instant = java.time.Instant.parse("2004-10-19T08:23:54Z") val zonedDateTime = java.time.ZonedDateTime.parse("2004-10-19T10:23:54+02:00[Europe/Prague]") @@ -77,7 +90,7 @@ class DatesTimesTest extends AnyFunSuite with DoobieTest { assert(expectedDatesTimes == result) } - test("DoobieTest WRITE") { + test("Writing different date/time types to the database") { val offsetDateTime = java.time.OffsetDateTime.parse("2004-10-19T08:23:54Z") val instant = java.time.Instant.parse("2004-10-19T08:23:54Z") val zonedDateTime = java.time.ZonedDateTime.parse("2004-10-19T10:23:54+02:00[Europe/Prague]") diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieMultipleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieMultipleResultFunctionTest.scala index 5851bc3c..55b3fa02 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieMultipleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieMultipleResultFunctionTest.scala @@ -36,7 +36,7 @@ class DoobieMultipleResultFunctionTest extends AnyFunSuite with DoobieTest { private val getActors = new GetActors()(Runs, new DoobieEngine(transactor)) - test("DoobieTest") { + test("Retrieving actor from database") { val expectedResultElem = Actor(49, "Pavel", "Marek") val results = getActors(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))).unsafeRunSync() assert(results.contains(expectedResultElem)) diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOptionalResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOptionalResultFunctionTest.scala index c115b6f0..ac2ddd11 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOptionalResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOptionalResultFunctionTest.scala @@ -36,7 +36,7 @@ class DoobieOptionalResultFunctionTest extends AnyFunSuite with DoobieTest { private val createActor = new GetActorById()(Runs, new DoobieEngine(transactor)) - test("DoobieTest") { + test("Retrieve actor by id from database") { val expectedResult = Some(Actor(49, "Pavel", "Marek")) val result = createActor(49).unsafeRunSync() assert(expectedResult == result) diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala index d391bde3..4823ff72 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala @@ -7,7 +7,10 @@ import doobie.util.Read import doobie.util.fragment.Fragment import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieSingleResultFunction +import za.co.absa.fadb.doobiedb.DoobieFunction.{DoobieSingleResultFunction, DoobieSingleResultFunctionWithStatus} +import za.co.absa.fadb.exceptions.DataConflictException +import za.co.absa.fadb.status.FunctionStatus +import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling import java.net.InetAddress import java.util.UUID @@ -38,11 +41,72 @@ class DoobieOtherTypesTest extends AnyFunSuite with DoobieTest { sql"SELECT * FROM ${Fragment.const(functionName)}($values)" } + class InsertOtherTypes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) + extends DoobieSingleResultFunctionWithStatus[OtherTypesData, Option[Int], IO] with StandardStatusHandling { + + override def sql(values: OtherTypesData)(implicit read: Read[StatusWithData[Option[Int]]]): Fragment = + sql""" + SELECT * FROM ${Fragment.const(functionName)}( + ${values.id}, + ${values.ltreeCol}::ltree, + ${values.inetCol}::inet, + ${values.macaddrCol}::macaddr, + ${values.hstoreCol}::hstore, + ${values.cidrCol}::cidr, + ${values.jsonCol}::json, + ${values.jsonbCol}::jsonb, + ${values.uuidCol}::uuid, + ${values.arrayCol}::integer[] + ) + """ + } + private val readOtherTypes = new ReadOtherTypes()(Runs, new DoobieEngine(transactor)) + private val insertOtherTypes = new InsertOtherTypes()(Runs, new DoobieEngine(transactor)) + + test("Reading other common data types from database") { + val expectedData = OtherTypesData( + id = 1, + ltreeCol = "Top.Science.Astronomy", + inetCol = InetAddress.getByName("192.168.1.1"), + macaddrCol = "08:00:2b:01:02:03", + hstoreCol = Map("key" -> "value"), + cidrCol = "192.168.1.0/24", + jsonCol = """{"key": "value"}""", + jsonbCol = """{"key": "value"}""", + uuidCol = UUID.fromString("b574cb0f-4790-4798-9b3f-824c7fab69dc"), + arrayCol = Array(1, 2, 3) + ) - test("DoobieTest") { val result = readOtherTypes(1).unsafeRunSync() - println(result) + + assert(result.id == expectedData.id) + assert(result.ltreeCol == expectedData.ltreeCol) + assert(result.inetCol == expectedData.inetCol) + assert(result.macaddrCol == expectedData.macaddrCol) + assert(result.hstoreCol == expectedData.hstoreCol) + assert(result.cidrCol == expectedData.cidrCol) + assert(result.jsonCol == expectedData.jsonCol) + assert(result.jsonbCol == expectedData.jsonbCol) + assert(result.uuidCol == expectedData.uuidCol) + assert(result.arrayCol sameElements expectedData.arrayCol) + } + + test("Writing other common data types to database") { + val data = OtherTypesData( + id = 3, + ltreeCol = "Top.Science.Astronomy", + inetCol = InetAddress.getByName("192.168.1.1"), + macaddrCol = "08:00:2b:01:02:03", + hstoreCol = Map("key" -> "value"), + cidrCol = "192.168.1/24", + jsonCol = """{"key": "value"}""", + jsonbCol = """{"key": "value"}""", + uuidCol = UUID.randomUUID(), + arrayCol = Array(1, 2, 3) + ) + val result = insertOtherTypes(data).unsafeRunSync() + assert(result == Left(DataConflictException(FunctionStatus(31, "data conflict")))) } } diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionTest.scala index 7d10b7fd..e7a005e1 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionTest.scala @@ -41,7 +41,7 @@ class DoobieSingleResultFunctionTest extends AnyFunSuite with DoobieTest { private val createActor = new CreateActor()(Runs, new DoobieEngine(transactor)) - test("DoobieTest") { + test("Inserting an actor into database") { assert(createActor(CreateActorRequestBody("Pavel", "Marek")).unsafeRunSync().isInstanceOf[Int]) } } diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala index acc1853d..d1fe9bbe 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala @@ -46,54 +46,42 @@ class DoobieSingleResultFunctionWithStatusTest extends AnyFunSuite with DoobieTe sql"SELECT * FROM ${Fragment.const(functionName)}(${values})" } + class ErrorIfNotOneWithStatus(functionNameOverride: String)(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) + extends DoobieSingleResultFunctionWithStatus[Int, Option[Int], IO](Some(functionNameOverride)) + with StandardStatusHandling { + + override def sql(values: Int)(implicit read: Read[StatusWithData[Option[Int]]]): Fragment = + sql"SELECT * FROM ${Fragment.const(functionName)}(${values})" + } + private val createActor = new CreateActor()(Runs, new DoobieEngine(transactor)) private val errorIfNotOne = new ErrorIfNotOne()(Runs, new DoobieEngine(transactor)) - test("DoobieTest with status handling") { + test("Creating actor within a function with status handling") { val requestBody = CreateActorRequestBody("Pavel", "Marek") val result = createActor(requestBody).unsafeRunSync() assert(result.isRight) - println(result) } - test("error if not one with status handling") { + test("Successful function call with status handling") { val result = errorIfNotOne(1).unsafeRunSync() assert(result.isRight) - println(result) } - test("error if not one with status handling - error") { - // throws null exception + test("Unsuccessful function call with status handling. Asserting on error when Int not wrapped in Option") { // SQL `NULL` read at column 3 (JDBC type Integer) but mapping is to a non-Option type; use Option here. Note that JDBC column indexing is 1-based. assertThrows[NonNullableColumnRead](errorIfNotOne(2).unsafeRunSync()) } - test("error if not one with status handling - error - null wrapped in option") { - class ErrorIfNotOne(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) - extends DoobieSingleResultFunctionWithStatus[Int, Option[Int], IO] - with StandardStatusHandling { + test("Unsuccessful function call with status handling. Asserting on error when Int wrapped in Option") { + val errorIfNotOne = new ErrorIfNotOneWithStatus("error_if_not_one")(Runs, new DoobieEngine(transactor)) - override def sql(values: Int)(implicit read: Read[StatusWithData[Option[Int]]]): Fragment = - sql"SELECT * FROM ${Fragment.const(functionName)}(${values})" - } - - val errorIfNotOne = new ErrorIfNotOne()(Runs, new DoobieEngine(transactor)) - - // does not throw because null is wrapped in option val result = errorIfNotOne(2).unsafeRunSync() assert(result.isLeft) } - test("error if not one with status handling - success - null wrapped in option") { - class ErrorIfNotOne(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) - extends DoobieSingleResultFunctionWithStatus[Int, Option[Int], IO] - with StandardStatusHandling { - - override def sql(values: Int)(implicit read: Read[StatusWithData[Option[Int]]]): Fragment = - sql"SELECT * FROM ${Fragment.const(functionName)}(${values})" - } - - val errorIfNotOne = new ErrorIfNotOne()(Runs, new DoobieEngine(transactor)) + test("Successful function call with status handling. Asserting on success when Int wrapped in Option") { + val errorIfNotOne = new ErrorIfNotOneWithStatus("error_if_not_one")(Runs, new DoobieEngine(transactor)) val result = errorIfNotOne(1).unsafeRunSync() result match { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala index 4ba6cc03..bf232195 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala @@ -37,7 +37,6 @@ trait DoobieTest { } protected val transactor = Transactor.fromDriverManager[IO]( -// "org.postgresql.ds.PGSimpleDataSource", "org.postgresql.Driver", "jdbc:postgresql://localhost:5432/movies", "postgres", diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala index c4ca6313..de69d719 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala @@ -9,14 +9,18 @@ import java.time.ZoneId // can't be named doobie due to a naming conflict package object doobiedb { - object implicits { - // Doobie does not provide a `Meta` instance for `ZonedDateTime` out of the box because database support for this type is not universal. - // This `Meta` instance converts between `ZonedDateTime` and `Timestamp`, using the system's default time zone. - // Please note that this might not be the correct behavior for your application if your database stores timestamps in a different time zone. - // Meta[A] is a convenience type class for introducing a symmetric `Get`/`Put` pair into implicit scope, and for deriving new symmetric pairs. - implicit val zonedDateTimeMeta: Meta[java.time.ZonedDateTime] = { - Meta[Timestamp].timap(t => t.toLocalDateTime.atZone(ZoneId.systemDefault()))(zdt => Timestamp.from(zdt.toInstant)) + object postgres { + + object implicits { + // Doobie does not provide a `Meta` instance for `ZonedDateTime` out of the box because database support for this type is not universal. + // This `Meta` instance converts between `ZonedDateTime` and `Timestamp`, using the system's default time zone. + // Please note that this might not be the correct behavior for your application if your database stores timestamps in a different time zone. + // Meta[A] is a convenience type class for introducing a symmetric `Get`/`Put` pair into implicit scope, and for deriving new symmetric pairs. + implicit val zonedDateTimeMeta: Meta[java.time.ZonedDateTime] = { + Meta[Timestamp].timap(t => t.toLocalDateTime.atZone(ZoneId.systemDefault()))(zdt => Timestamp.from(zdt.toInstant)) + } } + } } diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala index 4dde7557..be423072 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickMultipleResultFunctionTest.scala @@ -40,7 +40,7 @@ class SlickMultipleResultFunctionTest extends AnyFunSuite with SlickTest with Sc private val getActors = new GetActors()(Runs, new SlickPgEngine(db)) - test("SlickTest") { + test("Retrieving actors from database") { val expectedResultElem = Actor(49, "Pavel", "Marek") val results = getActors(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))) assert(results.futureValue.contains(expectedResultElem)) diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala index e4f761d0..25129eb2 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickOptionalResultFunctionTest.scala @@ -40,7 +40,7 @@ class SlickOptionalResultFunctionTest extends AnyFunSuite with SlickTest with Sc private val getActorById = new GetActorById()(Runs, new SlickPgEngine(db)) - test("SlickTest") { + test("Retrieving an actor by id from database") { val expectedResultElem = Some(Actor(49, "Pavel", "Marek")) val results = getActorById(49) assert(results.futureValue == expectedResultElem) diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala index 436b656b..da908201 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala @@ -25,7 +25,7 @@ class SlickSingleResultFunctionWithStatusTest extends AnyFunSuite with SlickTest private val createActor = new CreateActor()(Runs, new SlickPgEngine(db)) - test("SlickTest with status handling") { + test("Creating actor with status handling") { val requestBody = CreateActorRequestBody("Pavel", "Marek") val result = createActor(requestBody).futureValue assert(result.isRight) diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala index 16036067..9af78ba9 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala @@ -43,12 +43,22 @@ class SlickQueryWithStatus[R]( checkStatus: FunctionStatusWithData[PositionedResult] => Either[StatusException, PositionedResult] ) extends QueryWithStatus[PositionedResult, PositionedResult, R] { + /* + * Processes the status of the query and returns the status with data + * @param initialResult - the initial result of the query + * @return the status with data + */ override def processStatus(initialResult: PositionedResult): FunctionStatusWithData[PositionedResult] = { val status: Int = initialResult.<< val statusText: String = initialResult.<< FunctionStatusWithData(FunctionStatus(status, statusText), initialResult) } + /* + * Converts the status with data to either a status exception or the data + * @param statusWithData - the status with data + * @return either a status exception or the data + */ override def toStatusExceptionOrData( statusWithData: FunctionStatusWithData[PositionedResult] ): Either[StatusException, R] = { @@ -58,6 +68,11 @@ class SlickQueryWithStatus[R]( } } + /* + * Combines the processing of the status and the conversion of the status with data to either a status exception or the data + * @return the GetResult, that combines the processing of the status and the conversion of the status with data + * to either a status exception or the data + */ def getStatusExceptionOrData: GetResult[Either[StatusException, R]] = { GetResult(pr => processStatus(pr)).andThen(fs => toStatusExceptionOrData(fs)) } From 43b60c4edc5c2e51e0f62264350b7999f41c2e33 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Mon, 11 Dec 2023 10:51:00 +0100 Subject: [PATCH 41/90] scala 2.13 support --- build.sbt | 10 +++------- core/src/main/scala/za/co/absa/fadb/DBSchema.scala | 6 +++--- .../co/absa/fadb/naming/ExplicitNamingRequired.scala | 11 ++++++----- .../co/absa/fadb/status/handling/StatusHandling.scala | 2 +- .../implementations/StandardStatusHandling.scala | 2 +- .../za/co/absa/fadb/slick/FaDbPostgresProfile.scala | 4 ++-- 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/build.sbt b/build.sbt index 40f03f70..3749a8f3 100644 --- a/build.sbt +++ b/build.sbt @@ -19,11 +19,11 @@ import com.github.sbt.jacoco.report.JacocoReportSettings ThisBuild / organization := "za.co.absa.fa-db" -lazy val scala211 = "2.11.12" lazy val scala212 = "2.12.17" +lazy val scala213 = "2.13.11" -ThisBuild / scalaVersion := scala212 -ThisBuild / crossScalaVersions := Seq(scala211, scala212) +ThisBuild / scalaVersion := scala213 +ThisBuild / crossScalaVersions := Seq(scala212, scala213) ThisBuild / versionScheme := Some("early-semver") @@ -95,11 +95,7 @@ lazy val faDBDoobie = (project in file("doobie")) libraryDependencies ++= doobieDependencies(scalaVersion.value), javacOptions ++= commonJavacOptions, scalacOptions ++= commonScalacOptions, - (Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, // printScalaVersion is run with compile Defaults.itSettings, - compile / skip := !scalaVersion.value.startsWith("2.12"), // skip if not Scala 2.12 (does not work, but publishM2 does) - crossScalaVersions := Seq(scala212), - publishM2 := (if (scalaVersion.value.startsWith("2.12")) publishM2.value else ()) // publish only for Scala 2.12 ).dependsOn(faDbCore) .settings( jacocoReportSettings := commonJacocoReportSettings.withTitle(s"doobie:slick Jacoco Report - scala:${scalaVersion.value}"), diff --git a/core/src/main/scala/za/co/absa/fadb/DBSchema.scala b/core/src/main/scala/za/co/absa/fadb/DBSchema.scala index 30553300..8fadd6d2 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBSchema.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBSchema.scala @@ -27,15 +27,15 @@ import za.co.absa.fadb.naming.NamingConvention */ abstract class DBSchema(schemaNameOverride: Option[String] = None)(implicit val namingConvention: NamingConvention) { - def this(schemaNameOverride: String)(implicit namingConvention: NamingConvention) { + def this(schemaNameOverride: String)(implicit namingConvention: NamingConvention) = { this(Option(schemaNameOverride))(namingConvention) } - def this()(implicit namingConvention: NamingConvention) { + def this()(implicit namingConvention: NamingConvention) = { this(None)(namingConvention) } - def this(namingConvention: NamingConvention, schemaNameOverride: String) { + def this(namingConvention: NamingConvention, schemaNameOverride: String) = { this(Option(schemaNameOverride))(namingConvention) } diff --git a/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala b/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala index 5238387f..b0fbde12 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala @@ -19,15 +19,15 @@ package za.co.absa.fadb.naming import za.co.absa.fadb.exceptions.NamingException /** - * `ExplicitNamingRequired` is a [[NamingConvention]] that throws a [[NamingException]] for any string. + * `ExplicitNamingRequired` is a `NamingConvention` that throws a `NamingConvention` for any string. * This is used when explicit naming is required and no other naming convention should be applied. */ class ExplicitNamingRequired extends NamingConvention { /** - * Throws a [[NamingException]] with a message indicating that explicit naming is required. + * Throws a `NamingConvention` with a message indicating that explicit naming is required. * @param original - The original string. - * @return Nothing, as a [[NamingException]] is always thrown. + * @return Nothing, as a `NamingException` is always thrown. */ override def stringPerConvention(original: String): String = { val message = s"No convention for '$original', explicit naming required." @@ -36,13 +36,14 @@ class ExplicitNamingRequired extends NamingConvention { } /** - * `ExplicitNamingRequired.Implicits` provides an implicit [[NamingConvention]] instance that throws a [[NamingException]] for any string. + * `ExplicitNamingRequired.Implicits` provides an implicit `NamingConvention` instance that + * throws a `NamingException` for any string. */ object ExplicitNamingRequired { object Implicits { /** - * An implicit [[NamingConvention]] instance that throws a [[NamingException]] for any string. + * An implicit `NamingConvention` instance that throws a `NamingException` for any string. */ implicit val namingConvention: NamingConvention = new ExplicitNamingRequired() } diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala index 7f104e36..a03c5b4a 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala @@ -12,7 +12,7 @@ trait StatusHandling { /** * Checks the status of a function invocation. * @param statusWithData - The status of the function invocation with data. - * @return Either a [[StatusException]] if the status code indicates an error, or the data if the status code is successful. + * @return Either a `StatusException` if the status code indicates an error, or the data if the status code is successful. */ def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] } diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala index 579cf547..5aab9335 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala @@ -5,7 +5,7 @@ import za.co.absa.fadb.exceptions._ import za.co.absa.fadb.status.handling.StatusHandling /** - * `StandardStatusHandling` is a trait that extends the [[StatusHandling]] interface. + * `StandardStatusHandling` is a trait that extends the `StatusHandling` interface. * It provides a standard implementation for checking the status of a function invocation. */ trait StandardStatusHandling extends StatusHandling { diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/FaDbPostgresProfile.scala b/slick/src/main/scala/za/co/absa/fadb/slick/FaDbPostgresProfile.scala index f4fe92be..b0044d15 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/FaDbPostgresProfile.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/FaDbPostgresProfile.scala @@ -35,7 +35,7 @@ trait FaDbPostgresProfile with PgSearchSupport with PgUUIDSupport { - trait API + trait FaDbAPI extends super.API with ArrayImplicits with Date2DateTimeImplicitsDuration @@ -51,7 +51,7 @@ trait FaDbPostgresProfile with SimpleHStorePlainImplicits with UUIDPlainImplicits - override val api: API = new API {} + override val api: FaDbAPI = new FaDbAPI {} } object FaDbPostgresProfile extends FaDbPostgresProfile From 414cfa0af686e332311b8d544410cf7679d796fb Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Mon, 11 Dec 2023 10:56:32 +0100 Subject: [PATCH 42/90] added missing licenses --- .../co/absa/fadb/FunctionStatusWithData.scala | 16 ++++++++++++++++ .../absa/fadb/exceptions/StatusException.scala | 16 ++++++++++++++++ .../fadb/status/handling/StatusHandling.scala | 16 ++++++++++++++++ .../StandardStatusHandling.scala | 16 ++++++++++++++++ .../naming/ExplicitNamingRequiredSuite.scala | 16 ++++++++++++++++ .../implementations/SnakeCaseNamingSuite.scala | 16 ++++++++++++++++ .../StandardStatusHandlingTest.scala | 16 ++++++++++++++++ doobie/src/it/database/actors.ddl | 16 ++++++++++++++++ doobie/src/it/database/create_actor.sql | 16 ++++++++++++++++ doobie/src/it/database/dates_times.sql | 16 ++++++++++++++++ doobie/src/it/database/error_if_not_one.sql | 18 +++++++++++++++++- doobie/src/it/database/get_actor_by_id.sql | 16 ++++++++++++++++ doobie/src/it/database/get_actors.sql | 16 ++++++++++++++++ doobie/src/it/database/other_types.sql | 16 ++++++++++++++++ .../co/absa/fadb/doobiedb/DatesTimesTest.scala | 16 ++++++++++++++++ .../fadb/doobiedb/DoobieOtherTypesTest.scala | 16 ++++++++++++++++ .../co/absa/fadb/doobiedb/StatusWithData.scala | 16 ++++++++++++++++ .../za/co/absa/fadb/doobiedb/package.scala | 16 ++++++++++++++++ ...ickSingleResultFunctionWithStatusTest.scala | 16 ++++++++++++++++ 19 files changed, 305 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala b/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala index e210eb25..d93a4b85 100644 --- a/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala +++ b/core/src/main/scala/za/co/absa/fadb/FunctionStatusWithData.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb import za.co.absa.fadb.status.FunctionStatus diff --git a/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala b/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala index f98ee511..f25d5802 100644 --- a/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala +++ b/core/src/main/scala/za/co/absa/fadb/exceptions/StatusException.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.exceptions import za.co.absa.fadb.status.FunctionStatus diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala index a03c5b4a..82768ac5 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.status.handling import za.co.absa.fadb.FunctionStatusWithData diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala index 5aab9335..2e491082 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.status.handling.implementations import za.co.absa.fadb.FunctionStatusWithData diff --git a/core/src/test/scala/za/co/absa/fadb/naming/ExplicitNamingRequiredSuite.scala b/core/src/test/scala/za/co/absa/fadb/naming/ExplicitNamingRequiredSuite.scala index c8b43b8e..9c766ffa 100644 --- a/core/src/test/scala/za/co/absa/fadb/naming/ExplicitNamingRequiredSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/naming/ExplicitNamingRequiredSuite.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.naming import org.scalatest.matchers.should.Matchers diff --git a/core/src/test/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNamingSuite.scala b/core/src/test/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNamingSuite.scala index 45e70692..014c9294 100644 --- a/core/src/test/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNamingSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNamingSuite.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.naming.implementations import org.scalatest.matchers.should.Matchers diff --git a/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandlingTest.scala b/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandlingTest.scala index 9ee504c5..362d287e 100644 --- a/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandlingTest.scala +++ b/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandlingTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.status.handling.implementations import org.scalatest.funsuite.AnyFunSuiteLike diff --git a/doobie/src/it/database/actors.ddl b/doobie/src/it/database/actors.ddl index 3ffdc868..8f824a56 100644 --- a/doobie/src/it/database/actors.ddl +++ b/doobie/src/it/database/actors.ddl @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + CREATE TABLE IF NOT EXISTS runs.actors( actor_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, first_name VARCHAR(150) NOT NULL, diff --git a/doobie/src/it/database/create_actor.sql b/doobie/src/it/database/create_actor.sql index d8e0f098..a86243f2 100644 --- a/doobie/src/it/database/create_actor.sql +++ b/doobie/src/it/database/create_actor.sql @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + -- Function: runs.create_actor(TEXT, TEXT) -- This function creates a new actor in the 'runs.actors' table. diff --git a/doobie/src/it/database/dates_times.sql b/doobie/src/it/database/dates_times.sql index b17c334b..ea57abaa 100644 --- a/doobie/src/it/database/dates_times.sql +++ b/doobie/src/it/database/dates_times.sql @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + CREATE TABLE runs.date_time_types ( id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, offset_date_time TIMESTAMPTZ, diff --git a/doobie/src/it/database/error_if_not_one.sql b/doobie/src/it/database/error_if_not_one.sql index 24be9e19..4bf7bb9a 100644 --- a/doobie/src/it/database/error_if_not_one.sql +++ b/doobie/src/it/database/error_if_not_one.sql @@ -1,4 +1,20 @@ -CREATE OR REPLACE FUNCTION runs.error_if_not_one(p_input INT) +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +CREATE OR REPLACE FUNCTION runs.error_if_not_one(p_input INT) RETURNS TABLE( status INT, status_text TEXT, diff --git a/doobie/src/it/database/get_actor_by_id.sql b/doobie/src/it/database/get_actor_by_id.sql index 8a8039de..031d81d8 100644 --- a/doobie/src/it/database/get_actor_by_id.sql +++ b/doobie/src/it/database/get_actor_by_id.sql @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + /* * Function: runs.get_actor_by_id * diff --git a/doobie/src/it/database/get_actors.sql b/doobie/src/it/database/get_actors.sql index cc465d03..a6249a2d 100644 --- a/doobie/src/it/database/get_actors.sql +++ b/doobie/src/it/database/get_actors.sql @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + CREATE OR REPLACE FUNCTION runs.get_actors( i_first_name TEXT, i_last_name TEXT diff --git a/doobie/src/it/database/other_types.sql b/doobie/src/it/database/other_types.sql index a0bd88b9..41ff8b81 100644 --- a/doobie/src/it/database/other_types.sql +++ b/doobie/src/it/database/other_types.sql @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + CREATE TABLE runs.other_types ( id INT PRIMARY KEY, ltree_col LTREE, diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala index 58172924..70d2218e 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobiedb import cats.effect.IO diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala index 4823ff72..6681133e 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobiedb import cats.effect.IO diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala index d90db69a..e3565465 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobiedb /** diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala index de69d719..346490e2 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb import doobie.util.meta.Meta diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala index da908201..44a4412c 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickSingleResultFunctionWithStatusTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.slick import org.scalatest.concurrent.ScalaFutures From f5db2172af9cae2efcf7e4f7e69cf00eb5cbb120 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Mon, 11 Dec 2023 11:00:47 +0100 Subject: [PATCH 43/90] github action for 2.13 --- .github/workflows/build.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 997ee893..2193fdaa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,14 +29,14 @@ jobs: fail-fast: false matrix: include: - - scala: 2.11.12 - scalaShort: "2.11" - overall: 0.0 - changed: 80.0 - scala: 2.12.17 scalaShort: "2.12" overall: 0.0 changed: 80.0 + - scala: 2.13.11 + scalaShort: "2.13" + overall: 0.0 + changed: 80.0 name: Build and test steps: - name: Checkout code @@ -62,14 +62,14 @@ jobs: fail-fast: false matrix: include: - - scala: 2.11.12 - scalaShort: "2.11" - overall: 0.0 - changed: 80.0 - scala: 2.12.17 scalaShort: "2.12" overall: 0.0 changed: 80.0 + - scala: 2.13.11 + scalaShort: "2.13" + overall: 0.0 + changed: 80.0 name: JaCoCo Code Coverage ${{matrix.scala}} steps: - name: Checkout code From 03a4a587ac4216430362ef2671880faf89294f19 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Mon, 11 Dec 2023 11:58:39 +0100 Subject: [PATCH 44/90] clean-up --- core/src/main/scala/za/co/absa/fadb/Query.scala | 4 +++- project/Dependencies.scala | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/Query.scala b/core/src/main/scala/za/co/absa/fadb/Query.scala index 99af5743..b73acec7 100644 --- a/core/src/main/scala/za/co/absa/fadb/Query.scala +++ b/core/src/main/scala/za/co/absa/fadb/Query.scala @@ -26,7 +26,9 @@ trait Query[R] /** * The basis for all query types of [[DBEngine]] implementations with status - * @tparam R - the return type of the query + * @tparam A - the initial result type of the query + * @tparam B - the intermediate result type of the query + * @tparam R - the final return type of the query */ trait QueryWithStatus[A, B, R] { diff --git a/project/Dependencies.scala b/project/Dependencies.scala index b7c8b17b..16880df4 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -19,8 +19,8 @@ import sbt._ object Dependencies { private def commonDependencies(scalaVersion: String): Seq[ModuleID] = Seq( - "org.typelevel" %% "cats-core" % "2.9.0", // 2.0.0 latest for Scala 2.11, 2.9.0 used by Doobie - "org.typelevel" %% "cats-effect" % "3.5.0", // 2.0.0 latest for Scala 2.11, 2.9.0 used by Doobie + "org.typelevel" %% "cats-core" % "2.9.0", + "org.typelevel" %% "cats-effect" % "3.5.0", "org.scalatest" %% "scalatest" % "3.1.0" % "test,it", "org.scalatest" %% "scalatest-flatspec" % "3.2.0" % "test,it", "org.scalatestplus" %% "mockito-1-10" % "3.1.0.0" % "test,it" From 852bf6d2f04e6abc3dfae82214a671bc65804b9e Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Mon, 11 Dec 2023 14:17:01 +0100 Subject: [PATCH 45/90] doobie module part of coverage reporting --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2193fdaa..45649556 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -53,6 +53,7 @@ jobs: ${{ github.workspace }}/core/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml ${{ github.workspace }}/examples/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml ${{ github.workspace }}/slick/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml + ${{ github.workspace }}/doobie/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml key: ${{ runner.os }}-${{ matrix.scalaShort }}-${{ hashFiles('**/jacoco.xml') }} jacoco: @@ -80,6 +81,7 @@ jobs: ${{ github.workspace }}/core/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml ${{ github.workspace }}/examples/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml ${{ github.workspace }}/slick/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml + ${{ github.workspace }}/doobie/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml key: ${{ runner.os }}-${{ matrix.scalaShort }}-${{ hashFiles('**/jacoco.xml') }} - name: Setup Scala uses: olafurpg/setup-scala@v10 @@ -92,6 +94,7 @@ jobs: paths: > ${{ github.workspace }}/core/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml, ${{ github.workspace }}/slick/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml + ${{ github.workspace }}/doobie/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml # examples don't need code coverage - at least not now token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: ${{ matrix.overall }} From 7695b29bcfbdfb654dac4721802ff9b6ef90fe69 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Mon, 11 Dec 2023 15:12:02 +0100 Subject: [PATCH 46/90] ExplicitNamingRequiredTest --- .scalafmt.conf | 2 +- .../naming/ExplicitNamingRequiredTest.scala | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 core/src/test/scala/za/co/absa/fadb/naming/ExplicitNamingRequiredTest.scala diff --git a/.scalafmt.conf b/.scalafmt.conf index d174fd45..3f1d6cde 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,5 +1,5 @@ version = "3.5.3" -runner.dialect = scala212 +runner.dialect = scala213 maxColumn = 120 diff --git a/core/src/test/scala/za/co/absa/fadb/naming/ExplicitNamingRequiredTest.scala b/core/src/test/scala/za/co/absa/fadb/naming/ExplicitNamingRequiredTest.scala new file mode 100644 index 00000000..078142fd --- /dev/null +++ b/core/src/test/scala/za/co/absa/fadb/naming/ExplicitNamingRequiredTest.scala @@ -0,0 +1,32 @@ +package za.co.absa.fadb.naming + +import org.scalatest.funsuite.AnyFunSuiteLike +import za.co.absa.fadb.exceptions.NamingException + +class ExplicitNamingRequiredTest extends AnyFunSuiteLike { + + test("ExplicitNamingRequired throws NamingException for any string") { + val namingConvention = new ExplicitNamingRequired() + + val testStrings = Seq("test", "anotherTest", "123", "!@#$%^&*()") + + testStrings.foreach { testString => + assertThrows[NamingException] { + namingConvention.stringPerConvention(testString) + } + } + } + + test("Implicit NamingConvention throws NamingException for any string") { + import ExplicitNamingRequired.Implicits._ + + val testStrings = Seq("test", "anotherTest", "123", "!@#$%^&*()") + + testStrings.foreach { testString => + assertThrows[NamingException] { + namingConvention.stringPerConvention(testString) + } + } + } + +} From 2799189f55f32a89a5292c0d79beca00cf2aa3b8 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Mon, 11 Dec 2023 15:21:13 +0100 Subject: [PATCH 47/90] DBFunctionFabricTest --- .../za/co/absa/fadb/DBFunctionFabric.scala | 2 +- .../co/absa/fadb/DBFunctionFabricTest.scala | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 core/src/test/scala/za/co/absa/fadb/DBFunctionFabricTest.scala diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala b/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala index 60f2f623..1fb39a1c 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala @@ -47,7 +47,7 @@ abstract class DBFunctionFabric(functionNameOverride: Option[String])(implicit v /* * Generates a list of select columns for the function */ - protected def selectEntry: String = { + def selectEntry: String = { val fieldsSeq = fieldsToSelect if (fieldsSeq.isEmpty) { "*" diff --git a/core/src/test/scala/za/co/absa/fadb/DBFunctionFabricTest.scala b/core/src/test/scala/za/co/absa/fadb/DBFunctionFabricTest.scala new file mode 100644 index 00000000..24246930 --- /dev/null +++ b/core/src/test/scala/za/co/absa/fadb/DBFunctionFabricTest.scala @@ -0,0 +1,46 @@ +package za.co.absa.fadb + +import org.scalatest.funsuite.AnyFunSuiteLike + +import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ + +class DBFunctionFabricTest extends AnyFunSuiteLike { + + implicit object TestSchema extends DBSchema + + test("DBFunctionFabric should use provided function name") { + val fabric = new DBFunctionFabric(Some("testFunction")) {} + assert(fabric.functionName == "test_schema.testFunction") + } + + test("DBFunctionFabric should generate function name from class name if not provided") { + case class TestFunction() extends DBFunctionFabric(None) {} + val function = TestFunction() + assert(function.functionName == "test_schema.test_function") + } + + test("DBFunctionFabric should generate function name without schema if schema name is empty") { + implicit object TestSchema extends DBSchema("") + + val fabric = new DBFunctionFabric(Some("testFunction")) {} + assert(fabric.functionName == "testFunction") + } + + test("DBFunctionFabric should return empty sequence for fieldsToSelect by default") { + val fabric = new DBFunctionFabric(Some("testFunction")) {} + assert(fabric.fieldsToSelect.isEmpty) + } + + test("DBFunctionFabric should generate selectEntry correctly") { + val fabric = new DBFunctionFabric(Some("testFunction")) { + override def fieldsToSelect: Seq[String] = Seq("field1", "field2") + } + assert(fabric.selectEntry == "FNC.field1,FNC.field2") + } + + test("DBFunctionFabric should generate selectEntry as * if fieldsToSelect is empty") { + val fabric = new DBFunctionFabric(Some("testFunction")) {} + assert(fabric.selectEntry == "*") + } + +} From 9a2eeea961b60d4250ce860dac726c42fa7732b3 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 19 Dec 2023 14:20:49 +0100 Subject: [PATCH 48/90] pr comments addressed --- .scalafmt.conf | 2 +- .../scala/za/co/absa/fadb/DBFunction.scala | 1 - .../UserDefinedStatusHandling.scala | 4 ++-- .../StandardStatusHandlingTest.scala | 2 +- doobie/src/it/database/actors.ddl | 2 +- doobie/src/it/database/create_actor.sql | 2 +- doobie/src/it/database/dates_times.sql | 2 +- doobie/src/it/database/error_if_not_one.sql | 2 +- doobie/src/it/database/get_actor_by_id.sql | 2 +- doobie/src/it/database/get_actors.sql | 2 +- doobie/src/it/database/other_types.sql | 2 +- .../za/co/absa/fadb/doobiedb/DoobieTest.scala | 10 +++------- .../co/absa/fadb/doobiedb/DoobieEngine.scala | 2 ++ .../za/co/absa/fadb/doobiedb/DoobieQuery.scala | 2 +- .../co/absa/fadb/doobiedb/StatusWithData.scala | 2 +- .../za/co/absa/fadb/slick/SlickFunction.scala | 18 +++++++++++------- .../za/co/absa/fadb/slick/SlickPgEngine.scala | 10 ++++------ .../za/co/absa/fadb/slick/SlickQuery.scala | 14 +++++++------- 18 files changed, 40 insertions(+), 41 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 3f1d6cde..1a5a3a19 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -29,4 +29,4 @@ docstrings.wrap = no docstrings.removeEmpty = true align.openParenDefnSite = false -align.openParenCallSite = false \ No newline at end of file +align.openParenCallSite = false diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 32784a23..32245af6 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -79,7 +79,6 @@ abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverr * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. * @param schema - The schema the function belongs to. * @param dBEngine - The database engine that is supposed to execute the function (contains connection to the database). - * @param queryStatusHandling - The [[QueryStatusHandling]] instance that handles the status of the function invocation. * @tparam I - The type covering the input fields of the database function. * @tparam R - The type covering the returned fields from the database function. * @tparam E - The type of the [[DBEngine]] engine. diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/UserDefinedStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/UserDefinedStatusHandling.scala index f7d8c665..36af1c59 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/UserDefinedStatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/UserDefinedStatusHandling.scala @@ -1,5 +1,3 @@ -package za.co.absa.fadb.status.handling.implementations - /* * Copyright 2022 ABSA Group Limited * @@ -16,6 +14,8 @@ package za.co.absa.fadb.status.handling.implementations * limitations under the License. */ +package za.co.absa.fadb.status.handling.implementations + import za.co.absa.fadb.FunctionStatusWithData import za.co.absa.fadb.exceptions.{OtherStatusException, StatusException} import za.co.absa.fadb.status.handling.StatusHandling diff --git a/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandlingTest.scala b/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandlingTest.scala index 362d287e..69fa85c5 100644 --- a/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandlingTest.scala +++ b/core/src/test/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandlingTest.scala @@ -17,7 +17,7 @@ package za.co.absa.fadb.status.handling.implementations import org.scalatest.funsuite.AnyFunSuiteLike -import org.scalatest.matchers.should.Matchers.{a, convertToAnyShouldWrapper} +import org.scalatest.matchers.should.Matchers.convertToAnyShouldWrapper import za.co.absa.fadb.FunctionStatusWithData import za.co.absa.fadb.exceptions._ import za.co.absa.fadb.status.FunctionStatus diff --git a/doobie/src/it/database/actors.ddl b/doobie/src/it/database/actors.ddl index 8f824a56..8e2c1c6a 100644 --- a/doobie/src/it/database/actors.ddl +++ b/doobie/src/it/database/actors.ddl @@ -18,4 +18,4 @@ CREATE TABLE IF NOT EXISTS runs.actors( actor_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, first_name VARCHAR(150) NOT NULL, last_name VARCHAR(150) NOT NULL -); \ No newline at end of file +); diff --git a/doobie/src/it/database/create_actor.sql b/doobie/src/it/database/create_actor.sql index a86243f2..24fb50fe 100644 --- a/doobie/src/it/database/create_actor.sql +++ b/doobie/src/it/database/create_actor.sql @@ -50,4 +50,4 @@ BEGIN RETURN; END; $$ -LANGUAGE plpgsql; \ No newline at end of file +LANGUAGE plpgsql; diff --git a/doobie/src/it/database/dates_times.sql b/doobie/src/it/database/dates_times.sql index ea57abaa..84602104 100644 --- a/doobie/src/it/database/dates_times.sql +++ b/doobie/src/it/database/dates_times.sql @@ -119,4 +119,4 @@ BEGIN RETURN; END; -$$ LANGUAGE plpgsql; \ No newline at end of file +$$ LANGUAGE plpgsql; diff --git a/doobie/src/it/database/error_if_not_one.sql b/doobie/src/it/database/error_if_not_one.sql index 4bf7bb9a..8f29a6af 100644 --- a/doobie/src/it/database/error_if_not_one.sql +++ b/doobie/src/it/database/error_if_not_one.sql @@ -27,4 +27,4 @@ BEGIN RETURN QUERY SELECT 11 AS status, 'success' AS status_text, p_input AS input_value; END IF; END; -$$ LANGUAGE plpgsql; \ No newline at end of file +$$ LANGUAGE plpgsql; diff --git a/doobie/src/it/database/get_actor_by_id.sql b/doobie/src/it/database/get_actor_by_id.sql index 031d81d8..22df6fe0 100644 --- a/doobie/src/it/database/get_actor_by_id.sql +++ b/doobie/src/it/database/get_actor_by_id.sql @@ -48,4 +48,4 @@ RETURN QUERY SELECT A.actor_id, A.first_name, A.last_name WHERE A.actor_id = i_actor_id; END; $$ -LANGUAGE plpgsql; \ No newline at end of file +LANGUAGE plpgsql; diff --git a/doobie/src/it/database/get_actors.sql b/doobie/src/it/database/get_actors.sql index a6249a2d..bdf11186 100644 --- a/doobie/src/it/database/get_actors.sql +++ b/doobie/src/it/database/get_actors.sql @@ -33,4 +33,4 @@ RETURN QUERY SELECT A.actor_id, A.first_name, A.last_name ORDER BY A.actor_id ASC; END; $$ -LANGUAGE plpgsql; \ No newline at end of file +LANGUAGE plpgsql; diff --git a/doobie/src/it/database/other_types.sql b/doobie/src/it/database/other_types.sql index 41ff8b81..df3359d1 100644 --- a/doobie/src/it/database/other_types.sql +++ b/doobie/src/it/database/other_types.sql @@ -97,4 +97,4 @@ BEGIN END; RETURN NEXT; END; -$$ LANGUAGE plpgsql; \ No newline at end of file +$$ LANGUAGE plpgsql; diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala index bf232195..e8d8c08c 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala @@ -19,6 +19,7 @@ package za.co.absa.fadb.doobiedb import cats.effect.IO import doobie.util.log.{LogEvent, LogHandler} import doobie.util.transactor.Transactor +import doobie.util.transactor.Transactor.Aux import za.co.absa.fadb.DBSchema trait DoobieTest { @@ -29,14 +30,9 @@ trait DoobieTest { import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ object Runs extends DBSchema - val printSqlLogHandler: LogHandler[IO] = new LogHandler[IO] { - def run(logEvent: LogEvent): IO[Unit] = - IO { - println(logEvent.sql) - } - } + val printSqlLogHandler: LogHandler[IO] = (logEvent: LogEvent) => IO { println(logEvent.sql) } - protected val transactor = Transactor.fromDriverManager[IO]( + protected val transactor: Aux[IO, Unit] = Transactor.fromDriverManager[IO]( "org.postgresql.Driver", "jdbc:postgresql://localhost:5432/movies", "postgres", diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala index fbc6b57c..f45163fa 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala @@ -63,6 +63,8 @@ class DoobieEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DB private def executeQueryWithStatus[R]( query: QueryWithStatusType[R] )(implicit readStatusWithDataR: Read[StatusWithData[R]]): F[Either[StatusException, R]] = { + // .unique returns a single value, raising an exception if there is not exactly one row returned + // https://tpolecat.github.io/doobie/docs/04-Selecting.html query.fragment.query[StatusWithData[R]].unique.transact(transactor).map(query.getResultOrException) } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieQuery.scala index 7f632c70..a398d309 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieQuery.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieQuery.scala @@ -51,7 +51,7 @@ class DoobieQueryWithStatus[R]( * @return the status with data */ override def processStatus(initialResult: StatusWithData[R]): FunctionStatusWithData[R] = - FunctionStatusWithData(FunctionStatus(initialResult.status, initialResult.status_text), initialResult.data) + FunctionStatusWithData(FunctionStatus(initialResult.status, initialResult.statusText), initialResult.data) /* * Converts the status with data to either a status exception or the data diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala index e3565465..88153ae6 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala @@ -19,4 +19,4 @@ package za.co.absa.fadb.doobiedb /** * Represents a function status with data. */ -case class StatusWithData[R](status: Int, status_text: String, data: R) +case class StatusWithData[R](status: Int, statusText: String, data: R) diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala index 668039cf..06b143e3 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala @@ -32,6 +32,7 @@ import scala.concurrent.Future * @tparam R the result type of the function */ private[slick] trait SlickFunctionBase[I, R] { + /** * The `GetResult[R]` instance used to read the query result into `R`. */ @@ -49,6 +50,7 @@ private[slick] trait SlickFunctionBase[I, R] { } private[slick] trait SlickFunction[I, R] extends SlickFunctionBase[I, R] { + /** * Generates a `SlickQuery[R]` representing the SQL query for the function. * @@ -59,11 +61,12 @@ private[slick] trait SlickFunction[I, R] extends SlickFunctionBase[I, R] { } private[slick] trait SlickFunctionWithStatus[I, R] extends SlickFunctionBase[I, R] { + /** - * Generates a `SlickQueryWithStatus[R]` representing the SQL query for the function with status support. + * Generates a `SlickQueryWithStatus[R]` representing the SQL query for the function with status support. * - * @param status - the status to check - * @return - Success or failure the status means + * @param values the input values for the function + * @return the `SlickQueryWithStatus[R]` representing the SQL query */ protected def query(values: I): SlickQueryWithStatus[R] = new SlickQueryWithStatus[R](sql(values), slickConverter, checkStatus) @@ -73,12 +76,13 @@ private[slick] trait SlickFunctionWithStatus[I, R] extends SlickFunctionBase[I, } object SlickFunction { + /** * Class for Slick DB functions with status support. */ abstract class SlickSingleResultFunctionWithStatus[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, - DBEngine: SlickPgEngine + dBEngine: SlickPgEngine ) extends DBFunctionWithStatus[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunctionWithStatus[I, R] @@ -87,7 +91,7 @@ object SlickFunction { */ abstract class SlickSingleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, - DBEngine: SlickPgEngine + dBEngine: SlickPgEngine ) extends DBSingleResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunction[I, R] @@ -96,7 +100,7 @@ object SlickFunction { */ abstract class SlickMultipleResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, - DBEngine: SlickPgEngine + dBEngine: SlickPgEngine ) extends DBMultipleResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunction[I, R] @@ -105,7 +109,7 @@ object SlickFunction { */ abstract class SlickOptionalResultFunction[I, R](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, - DBEngine: SlickPgEngine + dBEngine: SlickPgEngine ) extends DBOptionalResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunction[I, R] } diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala index 9963778b..61eba4c3 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala @@ -16,15 +16,13 @@ package za.co.absa.fadb.slick +import cats.implicits._ +import slick.jdbc.PostgresProfile.api._ import za.co.absa.fadb.DBEngine +import za.co.absa.fadb.exceptions.StatusException import scala.concurrent.{ExecutionContext, Future} -import slick.jdbc.PostgresProfile.api._ -import cats.implicits._ - import scala.language.higherKinds -import slick.jdbc.{GetResult, PositionedResult} -import za.co.absa.fadb.exceptions.StatusException /** * [[DBEngine]] based on the Slick library in the Postgres flavor @@ -35,7 +33,7 @@ class SlickPgEngine(val db: Database)(implicit val executor: ExecutionContext) e /** * The type of Queries for Slick - * @tparam T - the return type of the query + * @tparam R - the return type of the query */ type QueryType[R] = SlickQuery[R] type QueryWithStatusType[R] = SlickQueryWithStatus[R] diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala index 9af78ba9..13b9f5bb 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala @@ -24,8 +24,8 @@ import za.co.absa.fadb.{FunctionStatusWithData, Query, QueryWithStatus} /** * SQL query representation for Slick * @param sql - the SQL query in Slick format - * @param getResult - the converting function, that converts the [[slick.jdbc.PositionedResult slick.PositionedResult]] (the result of Slick - * execution) into the desire `R` type + * @param getResult - function that converts the [[slick.jdbc.PositionedResult slick.PositionedResult]] + * (the result of Slick execution) into the desired `R` type * @tparam R - the return type of the query */ class SlickQuery[R](val sql: SQLActionBuilder, val getResult: GetResult[R]) extends Query[R] @@ -33,8 +33,8 @@ class SlickQuery[R](val sql: SQLActionBuilder, val getResult: GetResult[R]) exte /** * SQL query representation for Slick with status * @param sql - the SQL query in Slick format - * @param getResult - the converting function, that converts the [[slick.jdbc.PositionedResult slick.PositionedResult]] (the result of Slick - * execution) into the desire `R` type + * @param getResult - function that converts the [[slick.jdbc.PositionedResult slick.PositionedResult]] + * (the result of Slick execution) into the desired `R` type * @tparam R - the return type of the query */ class SlickQueryWithStatus[R]( @@ -43,7 +43,7 @@ class SlickQueryWithStatus[R]( checkStatus: FunctionStatusWithData[PositionedResult] => Either[StatusException, PositionedResult] ) extends QueryWithStatus[PositionedResult, PositionedResult, R] { - /* + /** * Processes the status of the query and returns the status with data * @param initialResult - the initial result of the query * @return the status with data @@ -54,7 +54,7 @@ class SlickQueryWithStatus[R]( FunctionStatusWithData(FunctionStatus(status, statusText), initialResult) } - /* + /** * Converts the status with data to either a status exception or the data * @param statusWithData - the status with data * @return either a status exception or the data @@ -68,7 +68,7 @@ class SlickQueryWithStatus[R]( } } - /* + /** * Combines the processing of the status and the conversion of the status with data to either a status exception or the data * @return the GetResult, that combines the processing of the status and the conversion of the status with data * to either a status exception or the data From e3c6e7f6da045e1d3f9d595d697d347a8fe972ba Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Tue, 19 Dec 2023 14:35:55 +0100 Subject: [PATCH 49/90] asisnaming test --- .../implementations/AsIsNamingTest.scala | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 core/src/test/scala/za/co/absa/fadb/naming/implementations/AsIsNamingTest.scala diff --git a/core/src/test/scala/za/co/absa/fadb/naming/implementations/AsIsNamingTest.scala b/core/src/test/scala/za/co/absa/fadb/naming/implementations/AsIsNamingTest.scala new file mode 100644 index 00000000..d7fabc1a --- /dev/null +++ b/core/src/test/scala/za/co/absa/fadb/naming/implementations/AsIsNamingTest.scala @@ -0,0 +1,20 @@ +package za.co.absa.fadb.naming.implementations + +import org.scalatest.funsuite.AnyFunSuiteLike +import org.scalatest.matchers.should.Matchers +import za.co.absa.fadb.naming.LettersCase + +class AsIsNamingTest extends AnyFunSuiteLike with Matchers { + + val asIsNaming = new AsIsNaming(LettersCase.AsIs) + + test("AsIsNaming should return the same string") { + val input = "testString" + val expectedOutput = "testString" + + val output = asIsNaming.stringPerConvention(input) + + output shouldEqual expectedOutput + } + +} From b8a433826ce6f94d7338750783c887d1a0b6ceb1 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Wed, 20 Dec 2023 15:58:27 +0100 Subject: [PATCH 50/90] downgrade to 1.0.0-RC2 as that's last version with compatible hikari --- .../absa/fadb/doobiedb/DatesTimesTest.scala | 284 +++++++++--------- .../za/co/absa/fadb/doobiedb/DoobieTest.scala | 4 - .../za/co/absa/fadb/doobiedb/package.scala | 2 +- project/Dependencies.scala | 6 +- .../fadb/slick/FaDbPostgresProfileSuite.scala | 4 +- 5 files changed, 152 insertions(+), 148 deletions(-) diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala index 70d2218e..5a006da7 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala @@ -1,138 +1,146 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb.doobiedb - -import cats.effect.IO -import cats.effect.unsafe.implicits.global -import doobie.implicits.toSqlInterpolator -import doobie.util.Read -import doobie.util.fragment.Fragment -import org.scalatest.funsuite.AnyFunSuite -import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobiedb.DoobieFunction.{DoobieSingleResultFunction, DoobieSingleResultFunctionWithStatus} -import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling - -class DatesTimesTest extends AnyFunSuite with DoobieTest { - - // these imports are needed - import doobie.postgres.implicits._ - import doobie.implicits.javasql._ - - // this import is needed for the implicit Meta[java.time.ZonedDateTime] - import za.co.absa.fadb.doobiedb.postgres.implicits.zonedDateTimeMeta - - case class DatesTimes( - offsetDateTime: java.time.OffsetDateTime, - instant: java.time.Instant, - zonedDateTime: java.time.ZonedDateTime, - localDateTime: java.time.LocalDateTime, - localDate: java.time.LocalDate, - localTime: java.time.LocalTime, - sqlDate: java.sql.Date, - sqlTime: java.sql.Time, - sqlTimestamp: java.sql.Timestamp, - utilDate: java.util.Date - ) - - class GetAllDateTimeTypes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) - extends DoobieSingleResultFunction[Int, DatesTimes, IO] { - - override def sql(values: Int)(implicit read: Read[DatesTimes]): Fragment = - sql"SELECT * FROM ${Fragment.const(functionName)}($values)" - } - - class InsertDatesTimes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) - extends DoobieSingleResultFunctionWithStatus[DatesTimes, Int, IO] with StandardStatusHandling { - - override def sql(values: DatesTimes)(implicit read: Read[StatusWithData[Int]]): Fragment = - sql""" - SELECT * FROM ${Fragment.const(functionName)}( - ${values.offsetDateTime}, - ${values.instant}, - ${values.zonedDateTime}, - ${values.localDateTime}, - ${values.localDate}, - ${values.localTime}, - ${values.sqlDate}, - ${values.sqlTime}, - ${values.sqlTimestamp}, - ${values.utilDate} - ) - """ - } - - private val getAllDateTimeTypes = new GetAllDateTimeTypes()(Runs, new DoobieEngine(transactor)) - private val insertDatesTimes = new InsertDatesTimes()(Runs, new DoobieEngine(transactor)) - - test("Reading different date/time types from the database") { - val offsetDateTime = java.time.OffsetDateTime.parse("2004-10-19T08:23:54Z") - val instant = java.time.Instant.parse("2004-10-19T08:23:54Z") - val zonedDateTime = java.time.ZonedDateTime.parse("2004-10-19T10:23:54+02:00[Europe/Prague]") - val localDateTime = java.time.LocalDateTime.parse("2004-10-19T10:23:54") - val localDate = java.time.LocalDate.parse("2004-10-19") - val localTime = java.time.LocalTime.parse("10:23:54") - val sqlDate = java.sql.Date.valueOf("2004-10-19") - val sqlTime = java.sql.Time.valueOf("10:23:54") - val sqlTimestamp = java.sql.Timestamp.valueOf("2004-10-19 10:23:54.0") - val utilDate = new java.util.Date(sqlDate.getTime) - - val expectedDatesTimes = DatesTimes( - offsetDateTime, - instant, - zonedDateTime, - localDateTime, - localDate, - localTime, - sqlDate, - sqlTime, - sqlTimestamp, - utilDate - ) - val result = getAllDateTimeTypes(1).unsafeRunSync() - assert(expectedDatesTimes == result) - } - - test("Writing different date/time types to the database") { - val offsetDateTime = java.time.OffsetDateTime.parse("2004-10-19T08:23:54Z") - val instant = java.time.Instant.parse("2004-10-19T08:23:54Z") - val zonedDateTime = java.time.ZonedDateTime.parse("2004-10-19T10:23:54+02:00[Europe/Prague]") - val localDateTime = java.time.LocalDateTime.parse("2004-10-19T10:23:54") - val localDate = java.time.LocalDate.parse("2004-10-19") - val localTime = java.time.LocalTime.parse("10:23:54") - val sqlDate = java.sql.Date.valueOf("2004-10-19") - val sqlTime = java.sql.Time.valueOf("10:23:54") - val sqlTimestamp = java.sql.Timestamp.valueOf("2004-10-19 10:23:54.0") - val utilDate = new java.util.Date(sqlDate.getTime) - - val datesTimes = DatesTimes( - offsetDateTime, - instant, - zonedDateTime, - localDateTime, - localDate, - localTime, - sqlDate, - sqlTime, - sqlTimestamp, - utilDate - ) - - val result = insertDatesTimes(datesTimes).unsafeRunSync() - assert(result.isRight) - } - -} +///* +// * Copyright 2022 ABSA Group Limited +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package za.co.absa.fadb.doobiedb +// +//import cats.effect.IO +//import cats.effect.unsafe.implicits.global +//import doobie.implicits.toSqlInterpolator +//import doobie.util.Read +//import doobie.util.fragment.Fragment +//import org.scalatest.funsuite.AnyFunSuite +//import za.co.absa.fadb.DBSchema +//import za.co.absa.fadb.doobiedb.DoobieFunction.{DoobieSingleResultFunction, DoobieSingleResultFunctionWithStatus} +//import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling +// +//class DatesTimesTest extends AnyFunSuite with DoobieTest { +// +// // these imports are needed +// import doobie.postgres._ +// import doobie.postgres.implicits._ +// import doobie.implicits.javasql._ // java.sql +// +// // Provides mappings for java.time.Instant +//// import doobie.implicits.legacy.instant._ +// +// // Provides mappings for java.time.LocalDate +//// import doobie.implicits.legacy.localdate._ +// +// +// // this import is needed for the implicit Meta[java.time.ZonedDateTime] +// import za.co.absa.fadb.doobiedb.postgres.implicits._ +// +// case class DatesTimes( +// offsetDateTime: java.time.OffsetDateTime, +// instant: java.time.Instant, +// zonedDateTime: java.time.ZonedDateTime, +// localDateTime: java.time.LocalDateTime, +// localDate: java.time.LocalDate, +// localTime: java.time.LocalTime, //conversion to class java.time.LocalTime from timestamptz not supported +// sqlDate: java.sql.Date, +// sqlTime: java.sql.Time, +// sqlTimestamp: java.sql.Timestamp, +// utilDate: java.util.Date +// ) +// +// class GetAllDateTimeTypes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) +// extends DoobieSingleResultFunction[Int, DatesTimes, IO] { +// +// override def sql(values: Int)(implicit read: Read[DatesTimes]): Fragment = +// sql"SELECT * FROM ${Fragment.const(functionName)}($values)" +// } +// +// class InsertDatesTimes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) +// extends DoobieSingleResultFunctionWithStatus[DatesTimes, Int, IO] with StandardStatusHandling { +// +// override def sql(values: DatesTimes)(implicit read: Read[StatusWithData[Int]]): Fragment = +// sql""" +// SELECT * FROM ${Fragment.const(functionName)}( +// ${values.offsetDateTime}, +// ${values.instant}, +// ${values.zonedDateTime}, +// ${values.localDateTime}, +// ${values.localDate}, +// ${values.localTime}, +// ${values.sqlDate}, +// ${values.sqlTime}, +// ${values.sqlTimestamp}, +// ${values.utilDate} +// ) +// """ +// } +// +// private val getAllDateTimeTypes = new GetAllDateTimeTypes()(Runs, new DoobieEngine(transactor)) +// private val insertDatesTimes = new InsertDatesTimes()(Runs, new DoobieEngine(transactor)) +// +// test("Reading different date/time types from the database") { +// val offsetDateTime = java.time.OffsetDateTime.parse("2004-10-19T08:23:54Z") +// val instant = java.time.Instant.parse("2004-10-19T08:23:54Z") +// val zonedDateTime = java.time.ZonedDateTime.parse("2004-10-19T10:23:54+02:00[Europe/Prague]") +// val localDateTime = java.time.LocalDateTime.parse("2004-10-19T10:23:54") +// val localDate = java.time.LocalDate.parse("2004-10-19") +// val localTime = java.time.LocalTime.parse("10:23:54") +// val sqlDate = java.sql.Date.valueOf("2004-10-19") +// val sqlTime = java.sql.Time.valueOf("10:23:54") +// val sqlTimestamp = java.sql.Timestamp.valueOf("2004-10-19 10:23:54.0") +// val utilDate = new java.util.Date(sqlDate.getTime) +// +// val expectedDatesTimes = DatesTimes( +// offsetDateTime, +// instant, +// zonedDateTime, +// localDateTime, +// localDate, +// localTime, +// sqlDate, +// sqlTime, +// sqlTimestamp, +// utilDate +// ) +// val result = getAllDateTimeTypes(1).unsafeRunSync() +// assert(expectedDatesTimes == result) +// } +// +// test("Writing different date/time types to the database") { +// val offsetDateTime = java.time.OffsetDateTime.parse("2004-10-19T08:23:54Z") +// val instant = java.time.Instant.parse("2004-10-19T08:23:54Z") +// val zonedDateTime = java.time.ZonedDateTime.parse("2004-10-19T10:23:54+02:00[Europe/Prague]") +// val localDateTime = java.time.LocalDateTime.parse("2004-10-19T10:23:54") +// val localDate = java.time.LocalDate.parse("2004-10-19") +// val localTime = java.time.LocalTime.parse("10:23:54") +// val sqlDate = java.sql.Date.valueOf("2004-10-19") +// val sqlTime = java.sql.Time.valueOf("10:23:54") +// val sqlTimestamp = java.sql.Timestamp.valueOf("2004-10-19 10:23:54.0") +// val utilDate = new java.util.Date(sqlDate.getTime) +// +// val datesTimes = DatesTimes( +// offsetDateTime, +// instant, +// zonedDateTime, +// localDateTime, +// localDate, +// localTime, +// sqlDate, +// sqlTime, +// sqlTimestamp, +// utilDate +// ) +// +// val result = insertDatesTimes(datesTimes).unsafeRunSync() +// assert(result.isRight) +// } +// +//} diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala index e8d8c08c..a3778c74 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala @@ -17,7 +17,6 @@ package za.co.absa.fadb.doobiedb import cats.effect.IO -import doobie.util.log.{LogEvent, LogHandler} import doobie.util.transactor.Transactor import doobie.util.transactor.Transactor.Aux import za.co.absa.fadb.DBSchema @@ -30,13 +29,10 @@ trait DoobieTest { import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ object Runs extends DBSchema - val printSqlLogHandler: LogHandler[IO] = (logEvent: LogEvent) => IO { println(logEvent.sql) } - protected val transactor: Aux[IO, Unit] = Transactor.fromDriverManager[IO]( "org.postgresql.Driver", "jdbc:postgresql://localhost:5432/movies", "postgres", "postgres", - Some(printSqlLogHandler) ) } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala index 346490e2..49536add 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala @@ -16,8 +16,8 @@ package za.co.absa.fadb -import doobie.util.meta.Meta import doobie.implicits.javasql._ +import doobie.util.meta.Meta import java.sql.Timestamp import java.time.ZoneId diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 16880df4..c1e9ca12 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -45,9 +45,9 @@ object Dependencies { def doobieDependencies(scalaVersion: String): Seq[ModuleID] = { commonDependencies(scalaVersion) ++ Seq( - "org.tpolecat" %% "doobie-core" % "1.0.0-RC4", - "org.tpolecat" %% "doobie-hikari" % "1.0.0-RC4", - "org.tpolecat" %% "doobie-postgres" % "1.0.0-RC4" + "org.tpolecat" %% "doobie-core" % "1.0.0-RC2", + "org.tpolecat" %% "doobie-hikari" % "1.0.0-RC2", + "org.tpolecat" %% "doobie-postgres" % "1.0.0-RC2" ) } diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala b/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala index c7bb3942..6db95896 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/FaDbPostgresProfileSuite.scala @@ -166,9 +166,9 @@ class FaDbPostgresProfileSuite extends AsyncFlatSpec { r.<<, r.<<, r.<<, - r.nextHStoreOption, + r.nextHStoreOption(), r.<<, - r.nextMacAddrOption + r.nextMacAddrOption() ) } } From 94d0212b8b82fac8b5fdfddcbecfbad7ce27efb6 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 21 Dec 2023 13:53:35 +0100 Subject: [PATCH 51/90] package object removed - obsolete, meta instances test --- .../doobiedb/AvailableMetaInstancesTest.scala | 103 +++++++ .../absa/fadb/doobiedb/DatesTimesTest.scala | 291 +++++++++--------- .../za/co/absa/fadb/doobiedb/package.scala | 42 --- 3 files changed, 248 insertions(+), 188 deletions(-) create mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobiedb/AvailableMetaInstancesTest.scala delete mode 100644 doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/AvailableMetaInstancesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/AvailableMetaInstancesTest.scala new file mode 100644 index 00000000..3d9c1149 --- /dev/null +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/AvailableMetaInstancesTest.scala @@ -0,0 +1,103 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.fadb.doobiedb + +import doobie.util.meta.Meta +import org.scalatest.funsuite.AnyFunSuite + +class AvailableMetaInstancesTest extends AnyFunSuite { + + test("Meta[java.time.OffsetDateTime] available from doobie.implicits.javatimedrivernative") { + import doobie.implicits.javatimedrivernative.JavaOffsetDateTimeMeta + Meta[java.time.OffsetDateTime] + } + + test("Meta[java.time.OffsetDateTime] available from doobie.postgres.implicits") { + import doobie.postgres.implicits._ + Meta[java.time.OffsetDateTime] + } + + test("Meta[java.time.Instant] from doobie.implicits.javatimedrivernative") { + import doobie.implicits.javatimedrivernative.JavaTimeInstantMeta + Meta[java.time.Instant] + } + + test("Meta[java.time.Instant] from doobie.postgres.implicits") { + import doobie.postgres.implicits._ + Meta[java.time.Instant] + } + + test("Meta[java.time.ZonedDateTime] from doobie.implicits.javatimedrivernative") { + import doobie.implicits.javatimedrivernative.JavaZonedDateTimeMeta + Meta[java.time.ZonedDateTime] + } + + test("Meta[java.time.ZonedDateTime] from doobie.postgres.implicits") { + import doobie.postgres.implicits._ + Meta[java.time.ZonedDateTime] + } + + test("Meta[java.time.LocalDateTime] from doobie.implicits.javatimedrivernative") { + import doobie.implicits.javatimedrivernative.JavaTimeLocalDateTimeMeta + Meta[java.time.LocalDateTime] + } + + test("Meta[java.time.LocalDateTime] from doobie.postgres.implicits") { + import doobie.postgres.implicits._ + Meta[java.time.LocalDateTime] + } + + test("Meta[java.time.LocalDate] from doobie.implicits.javatimedrivernative") { + import doobie.implicits.javatimedrivernative.JavaTimeLocalDateMeta + Meta[java.time.LocalDate] + } + + test("Meta[java.time.LocalDate] from doobie.postgres.implicits") { + import doobie.postgres.implicits._ + Meta[java.time.LocalDate] + } + + test("Meta[java.time.LocalTime] from doobie.implicits.javatimedrivernative") { + import doobie.implicits.javatimedrivernative.JavaLocalTimeMeta + Meta[java.time.LocalTime] + } + + test("Meta[java.time.LocalTime] from doobie.postgres.implicits") { + import doobie.postgres.implicits._ + Meta[java.time.LocalTime] + } + + test("Meta[java.sql.Date] from doobie.implicits.javasql") { + import doobie.implicits.javasql.DateMeta + Meta[java.sql.Date] + } + + test("Meta[java.sql.Time] from doobie.implicits.javasql") { + import doobie.implicits.javasql.TimeMeta + Meta[java.sql.Time] + } + + test("Meta[java.sql.Timestamp] from doobie.implicits.javasql") { + import doobie.implicits.javasql.TimestampMeta + Meta[java.sql.Timestamp] + } + + test("Meta[java.util.Date] out of the box") { + Meta[java.util.Date] + } + +} diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala index 5a006da7..bac51165 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala @@ -1,146 +1,145 @@ -///* -// * Copyright 2022 ABSA Group Limited -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -// -//package za.co.absa.fadb.doobiedb -// -//import cats.effect.IO -//import cats.effect.unsafe.implicits.global -//import doobie.implicits.toSqlInterpolator -//import doobie.util.Read -//import doobie.util.fragment.Fragment -//import org.scalatest.funsuite.AnyFunSuite -//import za.co.absa.fadb.DBSchema -//import za.co.absa.fadb.doobiedb.DoobieFunction.{DoobieSingleResultFunction, DoobieSingleResultFunctionWithStatus} -//import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling -// -//class DatesTimesTest extends AnyFunSuite with DoobieTest { -// -// // these imports are needed -// import doobie.postgres._ -// import doobie.postgres.implicits._ -// import doobie.implicits.javasql._ // java.sql -// -// // Provides mappings for java.time.Instant -//// import doobie.implicits.legacy.instant._ -// -// // Provides mappings for java.time.LocalDate -//// import doobie.implicits.legacy.localdate._ -// -// -// // this import is needed for the implicit Meta[java.time.ZonedDateTime] -// import za.co.absa.fadb.doobiedb.postgres.implicits._ -// -// case class DatesTimes( -// offsetDateTime: java.time.OffsetDateTime, -// instant: java.time.Instant, -// zonedDateTime: java.time.ZonedDateTime, -// localDateTime: java.time.LocalDateTime, -// localDate: java.time.LocalDate, -// localTime: java.time.LocalTime, //conversion to class java.time.LocalTime from timestamptz not supported -// sqlDate: java.sql.Date, -// sqlTime: java.sql.Time, -// sqlTimestamp: java.sql.Timestamp, -// utilDate: java.util.Date -// ) -// -// class GetAllDateTimeTypes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) -// extends DoobieSingleResultFunction[Int, DatesTimes, IO] { -// -// override def sql(values: Int)(implicit read: Read[DatesTimes]): Fragment = -// sql"SELECT * FROM ${Fragment.const(functionName)}($values)" -// } -// -// class InsertDatesTimes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) -// extends DoobieSingleResultFunctionWithStatus[DatesTimes, Int, IO] with StandardStatusHandling { -// -// override def sql(values: DatesTimes)(implicit read: Read[StatusWithData[Int]]): Fragment = -// sql""" -// SELECT * FROM ${Fragment.const(functionName)}( -// ${values.offsetDateTime}, -// ${values.instant}, -// ${values.zonedDateTime}, -// ${values.localDateTime}, -// ${values.localDate}, -// ${values.localTime}, -// ${values.sqlDate}, -// ${values.sqlTime}, -// ${values.sqlTimestamp}, -// ${values.utilDate} -// ) -// """ -// } -// -// private val getAllDateTimeTypes = new GetAllDateTimeTypes()(Runs, new DoobieEngine(transactor)) -// private val insertDatesTimes = new InsertDatesTimes()(Runs, new DoobieEngine(transactor)) -// -// test("Reading different date/time types from the database") { -// val offsetDateTime = java.time.OffsetDateTime.parse("2004-10-19T08:23:54Z") -// val instant = java.time.Instant.parse("2004-10-19T08:23:54Z") -// val zonedDateTime = java.time.ZonedDateTime.parse("2004-10-19T10:23:54+02:00[Europe/Prague]") -// val localDateTime = java.time.LocalDateTime.parse("2004-10-19T10:23:54") -// val localDate = java.time.LocalDate.parse("2004-10-19") -// val localTime = java.time.LocalTime.parse("10:23:54") -// val sqlDate = java.sql.Date.valueOf("2004-10-19") -// val sqlTime = java.sql.Time.valueOf("10:23:54") -// val sqlTimestamp = java.sql.Timestamp.valueOf("2004-10-19 10:23:54.0") -// val utilDate = new java.util.Date(sqlDate.getTime) -// -// val expectedDatesTimes = DatesTimes( -// offsetDateTime, -// instant, -// zonedDateTime, -// localDateTime, -// localDate, -// localTime, -// sqlDate, -// sqlTime, -// sqlTimestamp, -// utilDate -// ) -// val result = getAllDateTimeTypes(1).unsafeRunSync() -// assert(expectedDatesTimes == result) -// } -// -// test("Writing different date/time types to the database") { -// val offsetDateTime = java.time.OffsetDateTime.parse("2004-10-19T08:23:54Z") -// val instant = java.time.Instant.parse("2004-10-19T08:23:54Z") -// val zonedDateTime = java.time.ZonedDateTime.parse("2004-10-19T10:23:54+02:00[Europe/Prague]") -// val localDateTime = java.time.LocalDateTime.parse("2004-10-19T10:23:54") -// val localDate = java.time.LocalDate.parse("2004-10-19") -// val localTime = java.time.LocalTime.parse("10:23:54") -// val sqlDate = java.sql.Date.valueOf("2004-10-19") -// val sqlTime = java.sql.Time.valueOf("10:23:54") -// val sqlTimestamp = java.sql.Timestamp.valueOf("2004-10-19 10:23:54.0") -// val utilDate = new java.util.Date(sqlDate.getTime) -// -// val datesTimes = DatesTimes( -// offsetDateTime, -// instant, -// zonedDateTime, -// localDateTime, -// localDate, -// localTime, -// sqlDate, -// sqlTime, -// sqlTimestamp, -// utilDate -// ) -// -// val result = insertDatesTimes(datesTimes).unsafeRunSync() -// assert(result.isRight) -// } -// -//} +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.fadb.doobiedb + +import cats.effect.IO +import cats.effect.unsafe.implicits.global +import doobie.implicits.toSqlInterpolator +import doobie.util.Read +import doobie.util.fragment.Fragment +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.doobiedb.DoobieFunction.{DoobieSingleResultFunction, DoobieSingleResultFunctionWithStatus} +import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling + +class DatesTimesTest extends AnyFunSuite with DoobieTest { + + // these imports are needed + import doobie.postgres.implicits._ + import doobie.implicits.javasql._ + + case class DatesTimes( + offsetDateTime: java.time.OffsetDateTime, + instant: java.time.Instant, + zonedDateTime: java.time.ZonedDateTime, + localDateTime: java.time.LocalDateTime, + localDate: java.time.LocalDate, + localTime: java.time.LocalTime, + sqlDate: java.sql.Date, + sqlTime: java.sql.Time, + sqlTimestamp: java.sql.Timestamp, + utilDate: java.util.Date + ) + + class GetAllDateTimeTypes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) + extends DoobieSingleResultFunction[Int, DatesTimes, IO] { + + override def sql(values: Int)(implicit read: Read[DatesTimes]): Fragment = + sql"SELECT * FROM ${Fragment.const(functionName)}($values)" + } + + class InsertDatesTimes(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) + extends DoobieSingleResultFunctionWithStatus[DatesTimes, Int, IO] with StandardStatusHandling { + + override def sql(values: DatesTimes)(implicit read: Read[StatusWithData[Int]]): Fragment = + sql""" + SELECT * FROM ${Fragment.const(functionName)}( + ${values.offsetDateTime}, + ${values.instant}, + ${values.zonedDateTime}, + ${values.localDateTime}, + ${values.localDate}, + ${values.localTime}, + ${values.sqlDate}, + ${values.sqlTime}, + ${values.sqlTimestamp}, + ${values.utilDate} + ) + """ + } + + private val getAllDateTimeTypes = new GetAllDateTimeTypes()(Runs, new DoobieEngine(transactor)) + private val insertDatesTimes = new InsertDatesTimes()(Runs, new DoobieEngine(transactor)) + + test("Reading different date/time types from the database") { + val offsetDateTime = java.time.OffsetDateTime.parse("2004-10-19T08:23:54Z") + val instant = java.time.Instant.parse("2004-10-19T08:23:54Z") + val zonedDateTime = java.time.ZonedDateTime.parse("2004-10-19T08:23:54Z") + val localDateTime = java.time.LocalDateTime.parse("2004-10-19T10:23:54") + val localDate = java.time.LocalDate.parse("2004-10-19") + val localTime = java.time.LocalTime.parse("10:23:54") + val sqlDate = java.sql.Date.valueOf("2004-10-19") + val sqlTime = java.sql.Time.valueOf("10:23:54") + val sqlTimestamp = java.sql.Timestamp.valueOf("2004-10-19 10:23:54.0") + val utilDate = new java.util.Date(sqlDate.getTime) + + val expectedDatesTimes = DatesTimes( + offsetDateTime, + instant, + zonedDateTime, + localDateTime, + localDate, + localTime, + sqlDate, + sqlTime, + sqlTimestamp, + utilDate + ) + val result = getAllDateTimeTypes(1).unsafeRunSync() + + assert(expectedDatesTimes.offsetDateTime == result.offsetDateTime) + assert(expectedDatesTimes.instant == result.instant) + assert(result.zonedDateTime == expectedDatesTimes.zonedDateTime) + assert(expectedDatesTimes.localDateTime == result.localDateTime) + assert(expectedDatesTimes.localDate == result.localDate) + assert(expectedDatesTimes.localTime == result.localTime) + assert(expectedDatesTimes.sqlDate == result.sqlDate) + assert(expectedDatesTimes.sqlTime == result.sqlTime) + assert(expectedDatesTimes.sqlTimestamp == result.sqlTimestamp) + assert(expectedDatesTimes.utilDate == result.utilDate) + } + + test("Writing different date/time types to the database") { + val offsetDateTime = java.time.OffsetDateTime.parse("2004-10-19T08:23:54Z") + val instant = java.time.Instant.parse("2004-10-19T08:23:54Z") + val zonedDateTime = java.time.ZonedDateTime.parse("2004-10-19T10:23:54+02:00[Europe/Prague]") + val localDateTime = java.time.LocalDateTime.parse("2004-10-19T10:23:54") + val localDate = java.time.LocalDate.parse("2004-10-19") + val localTime = java.time.LocalTime.parse("10:23:54") + val sqlDate = java.sql.Date.valueOf("2004-10-19") + val sqlTime = java.sql.Time.valueOf("10:23:54") + val sqlTimestamp = java.sql.Timestamp.valueOf("2004-10-19 10:23:54.0") + val utilDate = new java.util.Date(sqlDate.getTime) + + val datesTimes = DatesTimes( + offsetDateTime, + instant, + zonedDateTime, + localDateTime, + localDate, + localTime, + sqlDate, + sqlTime, + sqlTimestamp, + utilDate + ) + + val result = insertDatesTimes(datesTimes).unsafeRunSync() + assert(result.isRight) + } + +} diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala deleted file mode 100644 index 49536add..00000000 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/package.scala +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2022 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.fadb - -import doobie.implicits.javasql._ -import doobie.util.meta.Meta - -import java.sql.Timestamp -import java.time.ZoneId - -// can't be named doobie due to a naming conflict -package object doobiedb { - - object postgres { - - object implicits { - // Doobie does not provide a `Meta` instance for `ZonedDateTime` out of the box because database support for this type is not universal. - // This `Meta` instance converts between `ZonedDateTime` and `Timestamp`, using the system's default time zone. - // Please note that this might not be the correct behavior for your application if your database stores timestamps in a different time zone. - // Meta[A] is a convenience type class for introducing a symmetric `Get`/`Put` pair into implicit scope, and for deriving new symmetric pairs. - implicit val zonedDateTimeMeta: Meta[java.time.ZonedDateTime] = { - Meta[Timestamp].timap(t => t.toLocalDateTime.atZone(ZoneId.systemDefault()))(zdt => Timestamp.from(zdt.toInstant)) - } - } - - } - -} From f647caaa74606ab2765d6d6d35d1327db5858ead Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 21 Dec 2023 15:12:33 +0100 Subject: [PATCH 52/90] docs how to set up things in zio app --- .../scala/za/co/absa/fadb/DBFunction.scala | 11 ++- doobie/README.md | 84 +++++++++++++++++++ .../co/absa/fadb/doobiedb/DoobieEngine.scala | 5 +- .../absa/fadb/doobiedb/DoobieFunction.scala | 8 +- .../za/co/absa/fadb/slick/SlickFunction.scala | 2 - 5 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 doobie/README.md diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 32245af6..ad8da2df 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -16,7 +16,6 @@ package za.co.absa.fadb -import cats.Monad import za.co.absa.fadb.exceptions.StatusException import za.co.absa.fadb.status.handling.StatusHandling @@ -32,7 +31,7 @@ import scala.language.higherKinds * @tparam E - The type of the [[DBEngine]] engine. * @tparam F - The type of the context in which the database function is executed. */ -abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None)( +abstract class DBFunction[I, R, E <: DBEngine[F], F[_]](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, val dBEngine: E ) extends DBFunctionFabric(functionNameOverride) { @@ -84,7 +83,7 @@ abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverr * @tparam E - The type of the [[DBEngine]] engine. * @tparam F - The type of the context in which the database function is executed. */ -abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None)( +abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, val dBEngine: E ) extends DBFunctionFabric(functionNameOverride) @@ -135,7 +134,7 @@ object DBFunction { * `DBMultipleResultFunction` is an abstract class that represents a database function returning multiple results. * It extends the [[DBFunction]] class and overrides the apply method to return a sequence of results. */ - abstract class DBMultipleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( + abstract class DBMultipleResultFunction[I, R, E <: DBEngine[F], F[_]]( functionNameOverride: Option[String] = None )(implicit schema: DBSchema, dBEngine: E) extends DBFunction[I, R, E, F](functionNameOverride) { @@ -159,7 +158,7 @@ object DBFunction { * `DBSingleResultFunction` is an abstract class that represents a database function returning a single result. * It extends the [[DBFunction]] class and overrides the apply method to return a single result. */ - abstract class DBSingleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( + abstract class DBSingleResultFunction[I, R, E <: DBEngine[F], F[_]]( functionNameOverride: Option[String] = None )(implicit schema: DBSchema, dBEngine: E) extends DBFunction[I, R, E, F](functionNameOverride) { @@ -182,7 +181,7 @@ object DBFunction { * `DBOptionalResultFunction` is an abstract class that represents a database function returning an optional result. * It extends the [[DBFunction]] class and overrides the apply method to return an optional result. */ - abstract class DBOptionalResultFunction[I, R, E <: DBEngine[F], F[_]: Monad]( + abstract class DBOptionalResultFunction[I, R, E <: DBEngine[F], F[_]]( functionNameOverride: Option[String] = None )(implicit schema: DBSchema, dBEngine: E) extends DBFunction[I, R, E, F](functionNameOverride) { diff --git a/doobie/README.md b/doobie/README.md new file mode 100644 index 00000000..d5cb432d --- /dev/null +++ b/doobie/README.md @@ -0,0 +1,84 @@ +### How to set things up in ZIO application + +Add the following dependencies to your `build.sbt` file. + +```scala +"za.co.absa.fa-db" %% "doobie" % "" +// if not already included as transitive dependency +"dev.zio" %% "zio-interop-cats" % "23.0.0.8" +``` + +Create a class for your database function and ZLayer in its companion object. + +```scala +class GetActorById(implicit schema: DBSchema, dbEngine: DoobieEngine[Task]) + extends DoobieOptionalResultFunction[Int, Actor, Task] { + + override def sql(values: Int)(implicit read: Read[Actor]): Fragment = + sql"SELECT actor_id, first_name, last_name FROM ${Fragment.const(functionName)}($values)" +} + +object GetActorById { + val layer: ZLayer[PostgresDatabaseProvider, Nothing, GetActorById] = ZLayer { + for { + dbProvider <- ZIO.service[PostgresDatabaseProvider] + } yield new GetActorById()(Runs, dbProvider.dbEngine) + } +} +``` + +Create a ZLayer for your doobie's Transactor. +Please note that HikariTransactor is a managed resource, and it needs to be closed when the application is shutting down. +Notice that the ZLayer requires a Scope to be provided. The Scope data type is the foundation of safe and composable resources handling in ZIO. +More on the topic can be found here https://zio.dev/reference/resource/scope/. + + +```scala +import com.zaxxer.hikari.HikariConfig +import doobie.hikari.HikariTransactor +import zio.Runtime.defaultBlockingExecutor +import zio._ +import zio.interop.catz._ // required for ZIO.toScopedZIO, provides also Async for zio.Task + +object TransactorProvider { + + val layer: ZLayer[Any with Scope, Throwable, HikariTransactor[Task]] = ZLayer { + for { + postgresConfig <- ZIO.config[PostgresConfig](PostgresConfig.config) + hikariConfig = { + val config = new HikariConfig() + config.setDriverClassName(postgresConfig.dataSourceClass) + config.setJdbcUrl( + s"jdbc:postgresql://${postgresConfig.serverName}:${postgresConfig.portNumber}/${postgresConfig.databaseName}" + ) + config.setUsername(postgresConfig.user) + config.setPassword(postgresConfig.password) + // configurable pool size + config.setMaximumPoolSize(postgresConfig.maxPoolSize) + config + } + // notice we are using the default blocking executor from zio.Runtime + // .toScopedZIO is an extension method that converts a managed resource to a scoped ZIO + xa <- HikariTransactor.fromHikariConfig[Task](hikariConfig, defaultBlockingExecutor.asExecutionContext).toScopedZIO + } yield xa + } +} +``` + +Provide default Scope for your application. + +```scala +object Main extends ZIOAppDefault with Server { + + override def run: ZIO[Any, Throwable, Unit] = + server + .provide( + // provided layers ... + // ... + // provided default scope + zio.Scope.default + ) +} +``` + +This way we can establish a connection to a database with managed connection pooling and rely on the default ZIO's blocking execution context and also properly clean up resources when application shuts down. diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala index f45163fa..cf7e105d 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala @@ -16,7 +16,6 @@ package za.co.absa.fadb.doobiedb -import cats.Monad import cats.effect.Async import cats.implicits._ import doobie._ @@ -34,9 +33,9 @@ import scala.language.higherKinds * `Async` is needed because Doobie requires it for non-blocking database operations. * * @param transactor the Doobie transactor for executing SQL queries - * @tparam F the effect type, which must have an `Async` and a `Monad` instance + * @tparam F the effect type, which must have an `Async` instance */ -class DoobieEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DBEngine[F] { +class DoobieEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[F] { /** The type of Doobie queries that produce `T` */ type QueryType[R] = DoobieQuery[R] diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala index 1cd366d5..d289b97f 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala @@ -101,7 +101,7 @@ object DoobieFunction { * @param readSelectWithStatus the `Read[StatusWithData[R]]` instance used to read the query result with status into `StatusWithData[R]` * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ - abstract class DoobieSingleResultFunctionWithStatus[I, R, F[_]: Async: Monad]( + abstract class DoobieSingleResultFunctionWithStatus[I, R, F[_]: Async]( functionNameOverride: Option[String] = None )(implicit override val schema: DBSchema, val dbEngine: DoobieEngine[F], @@ -120,7 +120,7 @@ object DoobieFunction { * @param readR the `Read[R]` instance used to read the query result into `R` * @tparam F the effect type, which must have an `Async` and a `Monad` instance */ - abstract class DoobieSingleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( + abstract class DoobieSingleResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] @@ -131,7 +131,7 @@ object DoobieFunction { * `DoobieMultipleResultFunction` is an abstract class that extends `DBMultipleResultFunction` with `DoobiePgEngine` as the engine type. * It represents a database function that returns multiple results. */ - abstract class DoobieMultipleResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( + abstract class DoobieMultipleResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] @@ -142,7 +142,7 @@ object DoobieFunction { * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` with `DoobiePgEngine` as the engine type. * It represents a database function that returns an optional result. */ - abstract class DoobieOptionalResultFunction[I, R, F[_]: Async: Monad](functionNameOverride: Option[String] = None)( + abstract class DoobieOptionalResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, val dbEngine: DoobieEngine[F], val readR: Read[R] diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala index 06b143e3..07000ad4 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala @@ -16,13 +16,11 @@ package za.co.absa.fadb.slick -import cats.implicits._ import slick.jdbc.{GetResult, SQLActionBuilder} import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBOptionalResultFunction, DBSingleResultFunction} import za.co.absa.fadb.exceptions.StatusException import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, FunctionStatusWithData} -import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future /** From cdb6feb0683c30e706d5ebc9283b9ebc4a666687 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 21 Dec 2023 15:23:28 +0100 Subject: [PATCH 53/90] docs how to set up things in zio app --- doobie/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doobie/README.md b/doobie/README.md index d5cb432d..648205db 100644 --- a/doobie/README.md +++ b/doobie/README.md @@ -65,6 +65,22 @@ object TransactorProvider { } ``` +Transactor can then be provided as a dependency to other ZLayers. + +```scala +class PostgresDatabaseProvider(val dbEngine: DoobieEngine[Task]) + +object PostgresDatabaseProvider { + val layer: RLayer[Transactor[Task], PostgresDatabaseProvider] = ZLayer { + for { + // access the transactor from the environment + transactor <- ZIO.service[Transactor[Task]] + doobieEngine <- ZIO.succeed(new DoobieEngine[Task](transactor)) + } yield new PostgresDatabaseProvider(doobieEngine) + } +} +``` + Provide default Scope for your application. ```scala From 712ac82ab92615be3baba720ca5b6c790f6a3bd0 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 22 Dec 2023 08:59:44 +0100 Subject: [PATCH 54/90] 2.13.11 -> 2.13.12 --- .github/workflows/build.yml | 4 ++-- build.sbt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 45649556..3b24f18a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,7 @@ jobs: scalaShort: "2.12" overall: 0.0 changed: 80.0 - - scala: 2.13.11 + - scala: 2.13.12 scalaShort: "2.13" overall: 0.0 changed: 80.0 @@ -67,7 +67,7 @@ jobs: scalaShort: "2.12" overall: 0.0 changed: 80.0 - - scala: 2.13.11 + - scala: 2.13.12 scalaShort: "2.13" overall: 0.0 changed: 80.0 diff --git a/build.sbt b/build.sbt index 3749a8f3..bf5474cf 100644 --- a/build.sbt +++ b/build.sbt @@ -20,7 +20,7 @@ import com.github.sbt.jacoco.report.JacocoReportSettings ThisBuild / organization := "za.co.absa.fa-db" lazy val scala212 = "2.12.17" -lazy val scala213 = "2.13.11" +lazy val scala213 = "2.13.12" ThisBuild / scalaVersion := scala213 ThisBuild / crossScalaVersions := Seq(scala212, scala213) From 6efd57119c1560b1503109b21ba1a4e583ab011b Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Fri, 22 Dec 2023 09:06:56 +0100 Subject: [PATCH 55/90] doc strings refactored --- core/src/main/scala/za/co/absa/fadb/DBEngine.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala index fdc08328..d282749d 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala @@ -39,7 +39,7 @@ abstract class DBEngine[F[_]: Monad] { /** * The actual query executioner of the queries of the engine * @param query - the query to execute - * @tparam R - return the of the query + * @tparam R - return type of the query * @return - sequence of the results of database query */ protected def run[R](query: QueryType[R]): F[Seq[R]] @@ -47,15 +47,15 @@ abstract class DBEngine[F[_]: Monad] { /** * The actual query executioner of the queries of the engine with status * @param query - the query to execute - * @tparam R - return the of the query - * @return - sequence of the results of database query + * @tparam R - return type of the query + * @return - result of database query with status */ def runWithStatus[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] /** * Public method to execute when query is expected to return multiple results * @param query - the query to execute - * @tparam R - return the of the query + * @tparam R - return type of the query * @return - sequence of the results of database query */ def fetchAll[R](query: QueryType[R]): F[Seq[R]] = { @@ -65,7 +65,7 @@ abstract class DBEngine[F[_]: Monad] { /** * Public method to execute when query is expected to return exactly one row * @param query - the query to execute - * @tparam R - return the of the query + * @tparam R - return type of the query * @return - sequence of the results of database query */ def fetchHead[R](query: QueryType[R]): F[R] = { @@ -75,7 +75,7 @@ abstract class DBEngine[F[_]: Monad] { /** * Public method to execute when query is expected to return one or no results * @param query - the query to execute - * @tparam R - return the of the query + * @tparam R - return type of the query * @return - sequence of the results of database query */ def fetchHeadOption[R](query: QueryType[R]): F[Option[R]] = { From a8a68608f48f64677eb59c636089aa6e06ace8d9 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sat, 23 Dec 2023 00:43:01 +0100 Subject: [PATCH 56/90] streaming functions for doobie and slick with tests --- .../scala/za/co/absa/fadb/DBFunction.scala | 36 +++++++++++++++++++ .../za/co/absa/fadb/DBStreamingEngine.scala | 9 +++++ .../DoobieStreamingResultFunctionTest.scala | 29 +++++++++++++++ .../absa/fadb/doobiedb/DoobieFunction.scala | 7 ++++ .../fadb/doobiedb/DoobieStreamingEngine.scala | 20 +++++++++++ project/Dependencies.scala | 4 ++- .../SlickStreamingResultFunctionTest.scala | 32 +++++++++++++++++ .../za/co/absa/fadb/slick/SlickFunction.scala | 9 ++++- .../za/co/absa/fadb/slick/SlickPgEngine.scala | 5 +++ .../fadb/slick/SlickPgStreamingEngine.scala | 17 +++++++++ 10 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala create mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieStreamingResultFunctionTest.scala create mode 100644 doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala create mode 100644 slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala create mode 100644 slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index ad8da2df..b7c82d5a 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -128,6 +128,28 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]](functionNameOv override def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] } +abstract class DBStreamingFunction[I, R, E <: DBStreamingEngine[F], F[_]](functionNameOverride: Option[String] = None)( + implicit override val schema: DBSchema, + val dbStreamingEngine: E +) extends DBFunctionFabric(functionNameOverride) { + + // A constructor that takes only the mandatory parameters and uses default values for the optional ones + def this()(implicit schema: DBSchema, dBEngine: E) = this(None) + + // A constructor that allows specifying the function name as a string, but not as an option + def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) + + protected def streamResults(values: I): fs2.Stream[F, R] = dbStreamingEngine.runStreaming(query(values)) + + /** + * Function to create the DB function call specific to the provided [[DBEngine]]. + * Expected to be implemented by the DBEngine specific mix-in. + * @param values - The values to pass over to the database function. + * @return - The SQL query in the format specific to the provided [[DBEngine]]. + */ + protected def query(values: I): dbStreamingEngine.QueryType[R] +} + object DBFunction { /** @@ -154,6 +176,20 @@ object DBFunction { def apply(values: I): F[Seq[R]] = multipleResults(values) } + abstract class DBStreamingResultFunction[I, R, E <: DBStreamingEngine[F], F[_]]( + functionNameOverride: Option[String] = None + )(implicit schema: DBSchema, dBStreamingEngine: E) + extends DBStreamingFunction[I, R, E, F](functionNameOverride) { + + // A constructor that takes only the mandatory parameters and uses default values for the optional ones + def this()(implicit schema: DBSchema, dBStreamingEngine: E) = this(None) + + // A constructor that allows specifying the function name as a string, but not as an option + def this(functionName: String)(implicit schema: DBSchema, dBStreamingEngine: E) = this(Some(functionName)) + + def apply(values: I): fs2.Stream[F, R] = streamResults(values) + } + /** * `DBSingleResultFunction` is an abstract class that represents a database function returning a single result. * It extends the [[DBFunction]] class and overrides the apply method to return a single result. diff --git a/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala b/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala new file mode 100644 index 00000000..7432967d --- /dev/null +++ b/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala @@ -0,0 +1,9 @@ +package za.co.absa.fadb + +abstract class DBStreamingEngine[F[_]] { + + type QueryType[R] <: Query[R] + + def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] + +} diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieStreamingResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieStreamingResultFunctionTest.scala new file mode 100644 index 00000000..23ea4abc --- /dev/null +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieStreamingResultFunctionTest.scala @@ -0,0 +1,29 @@ +package za.co.absa.fadb.doobiedb + +import cats.effect.IO +import cats.effect.unsafe.implicits.global +import doobie.implicits.toSqlInterpolator +import doobie.util.Read +import doobie.util.fragment.Fragment +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieStreamingResultFunction + +class DoobieStreamingResultFunctionTest extends AnyFunSuite with DoobieTest { + + class GetActors(implicit schema: DBSchema, dbEngine: DoobieStreamingEngine[IO]) + extends DoobieStreamingResultFunction[GetActorsQueryParameters, Actor, IO] { + + override def sql(values: GetActorsQueryParameters)(implicit read: Read[Actor]): Fragment = + sql"SELECT actor_id, first_name, last_name FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" + } + + private val getActors = new GetActors()(Runs, new DoobieStreamingEngine(transactor)) + + test("Retrieving actor from database") { + val expectedResultElem = Actor(49, "Pavel", "Marek") + val results = getActors(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))).take(10).compile.toList.unsafeRunSync() + assert(results.contains(expectedResultElem)) + } + +} diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala index d289b97f..76b672c4 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala @@ -138,6 +138,13 @@ object DoobieFunction { ) extends DBMultipleResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) with DoobieFunction[I, R] + abstract class DoobieStreamingResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)( + implicit override val schema: DBSchema, + val dbEngine: DoobieStreamingEngine[F], + val readR: Read[R] + ) extends DBStreamingResultFunction[I, R, DoobieStreamingEngine[F], F](functionNameOverride) + with DoobieFunction[I, R] + /** * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` with `DoobiePgEngine` as the engine type. * It represents a database function that returns an optional result. diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala new file mode 100644 index 00000000..0f3e63a5 --- /dev/null +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala @@ -0,0 +1,20 @@ +package za.co.absa.fadb.doobiedb + +import cats.effect.Async +import doobie.Transactor +import doobie.implicits.toDoobieStreamOps +import doobie.util.Read +import za.co.absa.fadb.DBStreamingEngine + +class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], chunkSize: Int = 512) extends DBStreamingEngine[F] { + + type QueryType[R] = DoobieQuery[R] + + override def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] = + executeStreamingQuery(query)(query.readR) + + private def executeStreamingQuery[R](query: QueryType[R])(implicit readR: Read[R]): fs2.Stream[F, R] = { + query.fragment.query[R].streamWithChunkSize(chunkSize).transact(transactor) + } + +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index c1e9ca12..da61bbca 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -21,6 +21,7 @@ object Dependencies { private def commonDependencies(scalaVersion: String): Seq[ModuleID] = Seq( "org.typelevel" %% "cats-core" % "2.9.0", "org.typelevel" %% "cats-effect" % "3.5.0", + "co.fs2" %% "fs2-core" % "3.7.0", "org.scalatest" %% "scalatest" % "3.1.0" % "test,it", "org.scalatest" %% "scalatest-flatspec" % "3.2.0" % "test,it", "org.scalatestplus" %% "mockito-1-10" % "3.1.0.0" % "test,it" @@ -39,7 +40,8 @@ object Dependencies { "org.slf4j" % "slf4j-nop" % "1.7.26", "com.typesafe.slick" %% "slick-hikaricp" % "3.3.3", "org.postgresql" % "postgresql" % "42.6.0", - "com.github.tminglei" %% "slick-pg" % "0.20.4" % Optional + "com.github.tminglei" %% "slick-pg" % "0.20.4" % Optional, + "co.fs2" %% "fs2-reactive-streams" % "3.9.3" ) } diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala new file mode 100644 index 00000000..22686648 --- /dev/null +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala @@ -0,0 +1,32 @@ +package za.co.absa.fadb.slick + +import cats.effect.IO +import cats.effect.unsafe.implicits.global +import org.scalatest.funsuite.AnyFunSuite +import slick.jdbc.SQLActionBuilder +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.slick.SlickFunction.SlickStreamingResultFunction +import za.co.absa.fadb.slick.FaDbPostgresProfile.api._ + +class SlickStreamingResultFunctionTest extends AnyFunSuite with SlickTest { + + class GetActors(implicit override val schema: DBSchema, val dbEngine: SlickPgStreamingEngine[IO]) + extends SlickStreamingResultFunction[GetActorsQueryParameters, Actor, IO] + with ActorSlickConverter { + + override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("actor_id", "first_name", "last_name") + + override protected def sql(values: GetActorsQueryParameters): SQLActionBuilder = { + sql"""SELECT #$selectEntry FROM #$functionName(${values.firstName},${values.lastName}) #$alias;""" + } + } + + private val getActors = new GetActors()(Runs, new SlickPgStreamingEngine[IO](db)) + + test("Retrieving actors from database") { + val expectedResultElem = Actor(49, "Pavel", "Marek") + val results = getActors(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))).take(10).compile.toList.unsafeRunSync() + assert(results.contains(expectedResultElem)) + } + +} diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala index 07000ad4..292e9412 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala @@ -16,8 +16,9 @@ package za.co.absa.fadb.slick +import cats.effect.kernel.Async import slick.jdbc.{GetResult, SQLActionBuilder} -import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBOptionalResultFunction, DBSingleResultFunction} +import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBOptionalResultFunction, DBSingleResultFunction, DBStreamingResultFunction} import za.co.absa.fadb.exceptions.StatusException import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, FunctionStatusWithData} @@ -102,6 +103,12 @@ object SlickFunction { ) extends DBMultipleResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunction[I, R] + abstract class SlickStreamingResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)(implicit + override val schema: DBSchema, + dBEngine: SlickPgStreamingEngine[F] + ) extends DBStreamingResultFunction[I, R, SlickPgStreamingEngine[F], F](functionNameOverride) + with SlickFunction[I, R] + /** * Class for Slick DB functions with optional result. */ diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala index 61eba4c3..30a82adf 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala @@ -49,6 +49,11 @@ class SlickPgEngine(val db: Database)(implicit val executor: ExecutionContext) e db.run(slickAction) } +// protected def runStreaming[R](query: QueryType[R]): fs2.Stream[Future, R] = { +// val slickPublisher = db.stream(query.sql.as[R](query.getResult)) +// slickPublisher.toStreamBuffered[Future](100) // parameter for buffer size +// } + /** * Execution using Slick with status * @param query - the Slick query to execute diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala new file mode 100644 index 00000000..38e98126 --- /dev/null +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala @@ -0,0 +1,17 @@ +package za.co.absa.fadb.slick + +import cats.effect.Async +import fs2.interop.reactivestreams.PublisherOps +import slick.jdbc.JdbcBackend.Database +import za.co.absa.fadb.DBStreamingEngine + +class SlickPgStreamingEngine[F[_]: Async](val db: Database, chunkSize: Int = 512) extends DBStreamingEngine[F] { + + type QueryType[R] = SlickQuery[R] + + def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] = { + val slickPublisher = db.stream(query.sql.as[R](query.getResult)) + slickPublisher.toStreamBuffered[F](chunkSize) + } + +} From e3bf15a129e89c46228f83d070b06fa1fbd2b569 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sat, 23 Dec 2023 00:50:13 +0100 Subject: [PATCH 57/90] streaming functions for doobie and slick with tests --- .../src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala | 5 ----- 1 file changed, 5 deletions(-) diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala index 30a82adf..61eba4c3 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala @@ -49,11 +49,6 @@ class SlickPgEngine(val db: Database)(implicit val executor: ExecutionContext) e db.run(slickAction) } -// protected def runStreaming[R](query: QueryType[R]): fs2.Stream[Future, R] = { -// val slickPublisher = db.stream(query.sql.as[R](query.getResult)) -// slickPublisher.toStreamBuffered[Future](100) // parameter for buffer size -// } - /** * Execution using Slick with status * @param query - the Slick query to execute From 50d7a3a578b07dd0e208dccae9dac51f8d3fc064 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sat, 23 Dec 2023 19:57:53 +0100 Subject: [PATCH 58/90] refactoring of dbfunction --- .../scala/za/co/absa/fadb/DBFunction.scala | 35 ++++++++++--------- .../absa/fadb/doobiedb/DoobieFunction.scala | 11 +++--- .../za/co/absa/fadb/slick/SlickFunction.scala | 9 +++-- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index b7c82d5a..4397870b 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -31,7 +31,7 @@ import scala.language.higherKinds * @tparam E - The type of the [[DBEngine]] engine. * @tparam F - The type of the context in which the database function is executed. */ -abstract class DBFunction[I, R, E <: DBEngine[F], F[_]](functionNameOverride: Option[String] = None)( +abstract private[fadb] class DBFunction[I, R, E <: DBEngine[F], F[_]](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, val dBEngine: E ) extends DBFunctionFabric(functionNameOverride) { @@ -128,6 +128,16 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]](functionNameOv override def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] } +/** + * `DBStreamingFunction` is an abstract class that represents a database function returning a stream of results. + * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. + * @param schema - The schema the function belongs to. + * @param dbStreamingEngine - The database engine that is supposed to execute the function (contains connection to the database). + * @tparam I - The type covering the input fields of the database function. + * @tparam R - The type covering the returned fields from the database function. + * @tparam E - The type of the [[DBStreamingEngine]] engine. + * @tparam F - The type of the context in which the database function is executed. + */ abstract class DBStreamingFunction[I, R, E <: DBStreamingEngine[F], F[_]](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, val dbStreamingEngine: E @@ -139,8 +149,6 @@ abstract class DBStreamingFunction[I, R, E <: DBStreamingEngine[F], F[_]](functi // A constructor that allows specifying the function name as a string, but not as an option def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) - protected def streamResults(values: I): fs2.Stream[F, R] = dbStreamingEngine.runStreaming(query(values)) - /** * Function to create the DB function call specific to the provided [[DBEngine]]. * Expected to be implemented by the DBEngine specific mix-in. @@ -148,6 +156,13 @@ abstract class DBStreamingFunction[I, R, E <: DBStreamingEngine[F], F[_]](functi * @return - The SQL query in the format specific to the provided [[DBEngine]]. */ protected def query(values: I): dbStreamingEngine.QueryType[R] + + /** + * Executes the database function and returns stream of results + * @param values The values to pass over to the database function + * @return A stream of results from the database function + */ + def apply(values: I): fs2.Stream[F, R] = dbStreamingEngine.runStreaming(query(values)) } object DBFunction { @@ -176,20 +191,6 @@ object DBFunction { def apply(values: I): F[Seq[R]] = multipleResults(values) } - abstract class DBStreamingResultFunction[I, R, E <: DBStreamingEngine[F], F[_]]( - functionNameOverride: Option[String] = None - )(implicit schema: DBSchema, dBStreamingEngine: E) - extends DBStreamingFunction[I, R, E, F](functionNameOverride) { - - // A constructor that takes only the mandatory parameters and uses default values for the optional ones - def this()(implicit schema: DBSchema, dBStreamingEngine: E) = this(None) - - // A constructor that allows specifying the function name as a string, but not as an option - def this(functionName: String)(implicit schema: DBSchema, dBStreamingEngine: E) = this(Some(functionName)) - - def apply(values: I): fs2.Stream[F, R] = streamResults(values) - } - /** * `DBSingleResultFunction` is an abstract class that represents a database function returning a single result. * It extends the [[DBFunction]] class and overrides the apply method to return a single result. diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala index 76b672c4..0eb8c37f 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala @@ -16,17 +16,16 @@ package za.co.absa.fadb.doobiedb -import cats.Monad import cats.effect.kernel.Async import doobie.util.Read import doobie.util.fragment.Fragment import za.co.absa.fadb.DBFunction._ import za.co.absa.fadb.exceptions.StatusException -import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, FunctionStatusWithData} +import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, DBStreamingFunction, FunctionStatusWithData} import scala.language.higherKinds -trait DoobieFunctionBase[R] { +private[doobiedb] trait DoobieFunctionBase[R] { /** * The `Read[R]` instance used to read the query result into `R`. */ @@ -138,11 +137,15 @@ object DoobieFunction { ) extends DBMultipleResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) with DoobieFunction[I, R] + /** + * `DoobieStreamingResultFunction` is an abstract class that extends `DBStreamingResultFunction` with `DoobiePgEngine` as the engine type. + * It represents a database function that returns a stream of results. + */ abstract class DoobieStreamingResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, val dbEngine: DoobieStreamingEngine[F], val readR: Read[R] - ) extends DBStreamingResultFunction[I, R, DoobieStreamingEngine[F], F](functionNameOverride) + ) extends DBStreamingFunction[I, R, DoobieStreamingEngine[F], F](functionNameOverride) with DoobieFunction[I, R] /** diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala index 292e9412..6a98022f 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala @@ -18,9 +18,9 @@ package za.co.absa.fadb.slick import cats.effect.kernel.Async import slick.jdbc.{GetResult, SQLActionBuilder} -import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBOptionalResultFunction, DBSingleResultFunction, DBStreamingResultFunction} +import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBOptionalResultFunction, DBSingleResultFunction} import za.co.absa.fadb.exceptions.StatusException -import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, FunctionStatusWithData} +import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, DBStreamingFunction, FunctionStatusWithData} import scala.concurrent.Future @@ -103,10 +103,13 @@ object SlickFunction { ) extends DBMultipleResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunction[I, R] + /** + * Class for Slick DB functions with streaming results. + */ abstract class SlickStreamingResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)(implicit override val schema: DBSchema, dBEngine: SlickPgStreamingEngine[F] - ) extends DBStreamingResultFunction[I, R, SlickPgStreamingEngine[F], F](functionNameOverride) + ) extends DBStreamingFunction[I, R, SlickPgStreamingEngine[F], F](functionNameOverride) with SlickFunction[I, R] /** From b542bfc66157df076cdf75213830aa7f2da407cf Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sat, 23 Dec 2023 20:05:53 +0100 Subject: [PATCH 59/90] add licences --- .../za/co/absa/fadb/DBStreamingEngine.scala | 31 +++++++++++++++ .../DoobieStreamingResultFunctionTest.scala | 21 +++++++++- .../fadb/doobiedb/DoobieStreamingEngine.scala | 39 ++++++++++++++++++- .../SlickStreamingResultFunctionTest.scala | 21 +++++++++- .../fadb/slick/SlickPgStreamingEngine.scala | 28 +++++++++++++ 5 files changed, 135 insertions(+), 5 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala b/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala index 7432967d..7a6b099d 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala @@ -1,9 +1,40 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb +/** + * `DBStreamingEngine` is an abstract class that represents a database engine. + * It provides methods to execute streaming queries from a database. + * @tparam F - The type of the context in which the database queries are executed. + */ abstract class DBStreamingEngine[F[_]] { + /** + * A type representing the (SQL) query within the engine + * @tparam R - the return type of the query + */ type QueryType[R] <: Query[R] + /** + * The actual query executioner of the queries of the engine + * @param query - the query to execute + * @tparam R - return type of the query + * @return - stream of the results of database query + */ def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] } diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieStreamingResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieStreamingResultFunctionTest.scala index 23ea4abc..7560c51c 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieStreamingResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieStreamingResultFunctionTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobiedb import cats.effect.IO @@ -12,7 +28,7 @@ import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieStreamingResultFunction class DoobieStreamingResultFunctionTest extends AnyFunSuite with DoobieTest { class GetActors(implicit schema: DBSchema, dbEngine: DoobieStreamingEngine[IO]) - extends DoobieStreamingResultFunction[GetActorsQueryParameters, Actor, IO] { + extends DoobieStreamingResultFunction[GetActorsQueryParameters, Actor, IO] { override def sql(values: GetActorsQueryParameters)(implicit read: Read[Actor]): Fragment = sql"SELECT actor_id, first_name, last_name FROM ${Fragment.const(functionName)}(${values.firstName}, ${values.lastName})" @@ -22,7 +38,8 @@ class DoobieStreamingResultFunctionTest extends AnyFunSuite with DoobieTest { test("Retrieving actor from database") { val expectedResultElem = Actor(49, "Pavel", "Marek") - val results = getActors(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))).take(10).compile.toList.unsafeRunSync() + val results = + getActors(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))).take(10).compile.toList.unsafeRunSync() assert(results.contains(expectedResultElem)) } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala index 0f3e63a5..870f073d 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.doobiedb import cats.effect.Async @@ -6,13 +22,34 @@ import doobie.implicits.toDoobieStreamOps import doobie.util.Read import za.co.absa.fadb.DBStreamingEngine -class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], chunkSize: Int = 512) extends DBStreamingEngine[F] { +/** + * `DoobieStreamingEngine` is a class that represents a database engine. + * It provides methods to execute streaming queries from a database. + * @tparam F - The type of the context in which the database queries are executed. + */ +class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], chunkSize: Int = 512) + extends DBStreamingEngine[F] { + /** The type of Doobie queries that produce `T` */ type QueryType[R] = DoobieQuery[R] + /** + * Executes a Doobie query and returns the result as an `fs2.Stream[F, R]`. + * + * @param query the Doobie query to execute + * @param readR the `Read[R]` instance used to read the query result into `R` + * @return the query result as an `fs2.Stream[F, R]` + */ override def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] = executeStreamingQuery(query)(query.readR) + /** + * Executes a Doobie query and returns the result as an `fs2.Stream[F, R]`. + * + * @param query the Doobie query to execute + * @param readR the `Read[R]` instance used to read the query result into `R` + * @return the query result as an `fs2.Stream[F, R]` + */ private def executeStreamingQuery[R](query: QueryType[R])(implicit readR: Read[R]): fs2.Stream[F, R] = { query.fragment.query[R].streamWithChunkSize(chunkSize).transact(transactor) } diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala index 22686648..002ca4f0 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.slick import cats.effect.IO @@ -11,7 +27,7 @@ import za.co.absa.fadb.slick.FaDbPostgresProfile.api._ class SlickStreamingResultFunctionTest extends AnyFunSuite with SlickTest { class GetActors(implicit override val schema: DBSchema, val dbEngine: SlickPgStreamingEngine[IO]) - extends SlickStreamingResultFunction[GetActorsQueryParameters, Actor, IO] + extends SlickStreamingResultFunction[GetActorsQueryParameters, Actor, IO] with ActorSlickConverter { override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("actor_id", "first_name", "last_name") @@ -25,7 +41,8 @@ class SlickStreamingResultFunctionTest extends AnyFunSuite with SlickTest { test("Retrieving actors from database") { val expectedResultElem = Actor(49, "Pavel", "Marek") - val results = getActors(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))).take(10).compile.toList.unsafeRunSync() + val results = + getActors(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))).take(10).compile.toList.unsafeRunSync() assert(results.contains(expectedResultElem)) } diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala index 38e98126..7400d64f 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.slick import cats.effect.Async @@ -5,10 +21,22 @@ import fs2.interop.reactivestreams.PublisherOps import slick.jdbc.JdbcBackend.Database import za.co.absa.fadb.DBStreamingEngine +/** + * `SlickStreamingEngine` is a class that represents a database engine. + * It provides methods to execute streaming queries from a database. + * @tparam F - The type of the context in which the database queries are executed. + */ class SlickPgStreamingEngine[F[_]: Async](val db: Database, chunkSize: Int = 512) extends DBStreamingEngine[F] { + /** The type of Slick queries that produce `T` */ type QueryType[R] = SlickQuery[R] + /** + * Executes a Slick query and returns the result as an `fs2.Stream[F, R]`. + * + * @param query the Slick query to execute + * @return the query result as an `fs2.Stream[F, R]` + */ def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] = { val slickPublisher = db.stream(query.sql.as[R](query.getResult)) slickPublisher.toStreamBuffered[F](chunkSize) From e4e8edc21f17feddc2be84963e7b07c0787ba550 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sat, 23 Dec 2023 20:11:58 +0100 Subject: [PATCH 60/90] add licences --- core/src/main/scala/za/co/absa/fadb/DBFunction.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 4397870b..1b51a249 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -31,7 +31,7 @@ import scala.language.higherKinds * @tparam E - The type of the [[DBEngine]] engine. * @tparam F - The type of the context in which the database function is executed. */ -abstract private[fadb] class DBFunction[I, R, E <: DBEngine[F], F[_]](functionNameOverride: Option[String] = None)( +private[fadb] abstract class DBFunction[I, R, E <: DBEngine[F], F[_]](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, val dBEngine: E ) extends DBFunctionFabric(functionNameOverride) { From 2cbc4f7a1ffe2517584b466729a607558eafaf76 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sat, 23 Dec 2023 20:14:27 +0100 Subject: [PATCH 61/90] scala.language.higherKinds --- core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala | 2 ++ .../scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala | 2 ++ .../scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala | 2 ++ 3 files changed, 6 insertions(+) diff --git a/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala b/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala index 7a6b099d..e8251412 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala @@ -16,6 +16,8 @@ package za.co.absa.fadb +import scala.language.higherKinds + /** * `DBStreamingEngine` is an abstract class that represents a database engine. * It provides methods to execute streaming queries from a database. diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala index 870f073d..703bfcce 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala @@ -22,6 +22,8 @@ import doobie.implicits.toDoobieStreamOps import doobie.util.Read import za.co.absa.fadb.DBStreamingEngine +import scala.language.higherKinds + /** * `DoobieStreamingEngine` is a class that represents a database engine. * It provides methods to execute streaming queries from a database. diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala index 7400d64f..90400d98 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala @@ -21,6 +21,8 @@ import fs2.interop.reactivestreams.PublisherOps import slick.jdbc.JdbcBackend.Database import za.co.absa.fadb.DBStreamingEngine +import scala.language.higherKinds + /** * `SlickStreamingEngine` is a class that represents a database engine. * It provides methods to execute streaming queries from a database. From bf49ae9ac0cc68d43ebb5abaaed288a71277b012 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sat, 23 Dec 2023 20:22:31 +0100 Subject: [PATCH 62/90] clean up docs --- core/src/main/scala/za/co/absa/fadb/DBFunction.scala | 10 +++++----- core/src/main/scala/za/co/absa/fadb/DBSchema.scala | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 1b51a249..3d4ab2ce 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -74,7 +74,7 @@ private[fadb] abstract class DBFunction[I, R, E <: DBEngine[F], F[_]](functionNa /** * `DBFunctionWithStatus` is an abstract class that represents a database function with a status. - * It extends the [[DBFunction]] class and adds handling for the status of the function invocation. + * It extends the `DBFunction` class and adds handling for the status of the function invocation. * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. * @param schema - The schema the function belongs to. * @param dBEngine - The database engine that is supposed to execute the function (contains connection to the database). @@ -100,7 +100,7 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]](functionNameOv /** * Executes the database function and returns multiple results. - * @param values + * @param values - The values to pass over to the database function. * @return A sequence of results from the database function. */ def apply(values: I): F[Either[StatusException, R]] = dBEngine.runWithStatus(query(values)) @@ -169,7 +169,7 @@ object DBFunction { /** * `DBMultipleResultFunction` is an abstract class that represents a database function returning multiple results. - * It extends the [[DBFunction]] class and overrides the apply method to return a sequence of results. + * It extends the `DBFunction` class and overrides the apply method to return a sequence of results. */ abstract class DBMultipleResultFunction[I, R, E <: DBEngine[F], F[_]]( functionNameOverride: Option[String] = None @@ -193,7 +193,7 @@ object DBFunction { /** * `DBSingleResultFunction` is an abstract class that represents a database function returning a single result. - * It extends the [[DBFunction]] class and overrides the apply method to return a single result. + * It extends the `DBFunction` class and overrides the apply method to return a single result. */ abstract class DBSingleResultFunction[I, R, E <: DBEngine[F], F[_]]( functionNameOverride: Option[String] = None @@ -216,7 +216,7 @@ object DBFunction { /** * `DBOptionalResultFunction` is an abstract class that represents a database function returning an optional result. - * It extends the [[DBFunction]] class and overrides the apply method to return an optional result. + * It extends the `DBFunction` class and overrides the apply method to return an optional result. */ abstract class DBOptionalResultFunction[I, R, E <: DBEngine[F], F[_]]( functionNameOverride: Option[String] = None diff --git a/core/src/main/scala/za/co/absa/fadb/DBSchema.scala b/core/src/main/scala/za/co/absa/fadb/DBSchema.scala index 8fadd6d2..4f119a11 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBSchema.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBSchema.scala @@ -40,7 +40,7 @@ abstract class DBSchema(schemaNameOverride: Option[String] = None)(implicit val } /** - * To easy pass over to [[DBFunction]] members of the schema + * To easy pass over to `DBFunction` members of the schema */ protected implicit val schema: DBSchema = this From 5d57f4aade0c94ce06456073fb30939c20dd52b4 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sat, 23 Dec 2023 20:27:30 +0100 Subject: [PATCH 63/90] fix compilation for scala 2.12 --- slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala index 6a98022f..0377994a 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala @@ -24,6 +24,8 @@ import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, DBStreamingFunction, Fun import scala.concurrent.Future +import scala.language.higherKinds + /** * Base class for Slick DB functions. * From 32e9bf87b5897d7007a566fd33e9039a822b890e Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Sun, 24 Dec 2023 12:32:31 +0100 Subject: [PATCH 64/90] fix docs strings --- .../za/co/absa/fadb/doobiedb/DoobieFunction.scala | 14 +++++++++----- .../absa/fadb/doobiedb/DoobieStreamingEngine.scala | 2 +- .../absa/fadb/slick/SlickPgStreamingEngine.scala | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala index 0eb8c37f..4201ad98 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala @@ -90,7 +90,8 @@ private[doobiedb] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBas */ object DoobieFunction { /** - * `DoobieSingleResultFunctionWithStatus` is an abstract class that extends `DBSingleResultFunctionWithStatus` with `DoobiePgEngine` as the engine type. + * `DoobieSingleResultFunctionWithStatus` is an abstract class that extends `DBSingleResultFunctionWithStatus` + * with `DoobieEngine` as the engine type. * It represents a database function that returns a single result with status. * * @param functionNameOverride the optional override for the function name @@ -110,7 +111,7 @@ object DoobieFunction { with DoobieFunctionWithStatus[I, R] /** - * `DoobieSingleResultFunction` is an abstract class that extends `DBSingleResultFunction` with `DoobiePgEngine` as the engine type. + * `DoobieSingleResultFunction` is an abstract class that extends `DBSingleResultFunction` with `DoobieEngine` as the engine type. * It represents a database function that returns a single result. * * @param functionNameOverride the optional override for the function name @@ -127,7 +128,8 @@ object DoobieFunction { with DoobieFunction[I, R] /** - * `DoobieMultipleResultFunction` is an abstract class that extends `DBMultipleResultFunction` with `DoobiePgEngine` as the engine type. + * `DoobieMultipleResultFunction` is an abstract class that extends `DBMultipleResultFunction` + * with `DoobieEngine` as the engine type. * It represents a database function that returns multiple results. */ abstract class DoobieMultipleResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)( @@ -138,7 +140,8 @@ object DoobieFunction { with DoobieFunction[I, R] /** - * `DoobieStreamingResultFunction` is an abstract class that extends `DBStreamingResultFunction` with `DoobiePgEngine` as the engine type. + * `DoobieStreamingResultFunction` is an abstract class that extends `DBStreamingFunction` + * with `DoobieStreamingEngine` as the engine type. * It represents a database function that returns a stream of results. */ abstract class DoobieStreamingResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)( @@ -149,7 +152,8 @@ object DoobieFunction { with DoobieFunction[I, R] /** - * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` with `DoobiePgEngine` as the engine type. + * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` + * with `DoobieEngine` as the engine type. * It represents a database function that returns an optional result. */ abstract class DoobieOptionalResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)( diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala index 703bfcce..c17ff87e 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala @@ -32,7 +32,7 @@ import scala.language.higherKinds class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], chunkSize: Int = 512) extends DBStreamingEngine[F] { - /** The type of Doobie queries that produce `T` */ + /** The type of Doobie queries that produce `R` */ type QueryType[R] = DoobieQuery[R] /** diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala index 90400d98..7c575e2d 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala @@ -30,7 +30,7 @@ import scala.language.higherKinds */ class SlickPgStreamingEngine[F[_]: Async](val db: Database, chunkSize: Int = 512) extends DBStreamingEngine[F] { - /** The type of Slick queries that produce `T` */ + /** The type of Slick queries that produce `R` */ type QueryType[R] = SlickQuery[R] /** From 780a2bd382894bc1be065e2dd315943b8bcc3d42 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Wed, 27 Dec 2023 12:09:54 +0100 Subject: [PATCH 65/90] Added alternative apply to DBStreamingFunction that allows for explicit chunkSize definition on the function level. Refactored the DBStreamingEngine as needed. --- .../scala/za/co/absa/fadb/DBFunction.scala | 10 ++++++++++ .../za/co/absa/fadb/DBStreamingEngine.scala | 17 +++++++++++++---- .../fadb/doobiedb/DoobieStreamingEngine.scala | 18 +++++++++++++++--- .../SlickStreamingResultFunctionTest.scala | 2 +- .../fadb/slick/SlickPgStreamingEngine.scala | 14 +++++++++++++- 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 3d4ab2ce..be3a11b1 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -163,6 +163,16 @@ abstract class DBStreamingFunction[I, R, E <: DBStreamingEngine[F], F[_]](functi * @return A stream of results from the database function */ def apply(values: I): fs2.Stream[F, R] = dbStreamingEngine.runStreaming(query(values)) + + /** + * Executes the database function and returns stream of results. Allows to specify chunk size. + * @param values The values to pass over to the database function + * @param chunkSize The chunk size to use for the stream + * @return A stream of results from the database function + */ + def apply(values: I, chunkSize: Int): fs2.Stream[F, R] = { + dbStreamingEngine.runStreamingWithChunkSize(query(values), chunkSize) + } } object DBFunction { diff --git a/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala b/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala index e8251412..a265fa90 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala @@ -32,11 +32,20 @@ abstract class DBStreamingEngine[F[_]] { type QueryType[R] <: Query[R] /** - * The actual query executioner of the queries of the engine - * @param query - the query to execute - * @tparam R - return type of the query - * @return - stream of the results of database query + * Executes a query and returns the result as an `fs2.Stream[F, R]`. + * + * @param query the query to execute + * @return the query result as an `fs2.Stream[F, R]` */ def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] + /** + * Executes a query and returns the result as an `fs2.Stream[F, R]`. + * + * @param query the query to execute + * @param chunkSize the chunk size to use when streaming the query result + * @return the query result as an `fs2.Stream[F, R]` + */ + def runStreamingWithChunkSize[R](query: QueryType[R], chunkSize: Int): fs2.Stream[F, R] + } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala index c17ff87e..e4983eb2 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieStreamingEngine.scala @@ -29,7 +29,7 @@ import scala.language.higherKinds * It provides methods to execute streaming queries from a database. * @tparam F - The type of the context in which the database queries are executed. */ -class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], chunkSize: Int = 512) +class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], defaultChunkSize: Int = 512) extends DBStreamingEngine[F] { /** The type of Doobie queries that produce `R` */ @@ -43,16 +43,28 @@ class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], chunkSiz * @return the query result as an `fs2.Stream[F, R]` */ override def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] = - executeStreamingQuery(query)(query.readR) + executeStreamingQuery(query, defaultChunkSize)(query.readR) /** * Executes a Doobie query and returns the result as an `fs2.Stream[F, R]`. * * @param query the Doobie query to execute + * @param chunkSize the chunk size to use when streaming the query result * @param readR the `Read[R]` instance used to read the query result into `R` * @return the query result as an `fs2.Stream[F, R]` */ - private def executeStreamingQuery[R](query: QueryType[R])(implicit readR: Read[R]): fs2.Stream[F, R] = { + override def runStreamingWithChunkSize[R](query: QueryType[R], chunkSize: Int): fs2.Stream[F, R] = + executeStreamingQuery(query, chunkSize)(query.readR) + + /** + * Executes a Doobie query and returns the result as an `fs2.Stream[F, R]`. + * + * @param query the Doobie query to execute + * @param chunkSize the chunk size to use when streaming the query result + * @param readR the `Read[R]` instance used to read the query result into `R` + * @return the query result as an `fs2.Stream[F, R]` + */ + private def executeStreamingQuery[R](query: QueryType[R], chunkSize: Int)(implicit readR: Read[R]): fs2.Stream[F, R] = { query.fragment.query[R].streamWithChunkSize(chunkSize).transact(transactor) } diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala b/slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala index 002ca4f0..cd4044d7 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala +++ b/slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala @@ -42,7 +42,7 @@ class SlickStreamingResultFunctionTest extends AnyFunSuite with SlickTest { test("Retrieving actors from database") { val expectedResultElem = Actor(49, "Pavel", "Marek") val results = - getActors(GetActorsQueryParameters(Some("Pavel"), Some("Marek"))).take(10).compile.toList.unsafeRunSync() + getActors(GetActorsQueryParameters(Some("Pavel"), Some("Marek")), 1024).take(10).compile.toList.unsafeRunSync() assert(results.contains(expectedResultElem)) } diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala index 7c575e2d..f7d7cff5 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala @@ -28,7 +28,7 @@ import scala.language.higherKinds * It provides methods to execute streaming queries from a database. * @tparam F - The type of the context in which the database queries are executed. */ -class SlickPgStreamingEngine[F[_]: Async](val db: Database, chunkSize: Int = 512) extends DBStreamingEngine[F] { +class SlickPgStreamingEngine[F[_]: Async](val db: Database, defaultChunkSize: Int = 512) extends DBStreamingEngine[F] { /** The type of Slick queries that produce `R` */ type QueryType[R] = SlickQuery[R] @@ -40,6 +40,18 @@ class SlickPgStreamingEngine[F[_]: Async](val db: Database, chunkSize: Int = 512 * @return the query result as an `fs2.Stream[F, R]` */ def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] = { + val slickPublisher = db.stream(query.sql.as[R](query.getResult)) + slickPublisher.toStreamBuffered[F](defaultChunkSize) + } + + /** + * Executes a Slick query and returns the result as an `fs2.Stream[F, R]`. + * + * @param query the Slick query to execute + * @param chunkSize the chunk size to use when streaming the query result + * @return the query result as an `fs2.Stream[F, R]` + */ + def runStreamingWithChunkSize[R](query: QueryType[R], chunkSize: Int): fs2.Stream[F, R] = { val slickPublisher = db.stream(query.sql.as[R](query.getResult)) slickPublisher.toStreamBuffered[F](chunkSize) } From 8144e8922bd3800a72dff2af89ff8f4e6982a3ef Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Wed, 27 Dec 2023 18:19:08 +0100 Subject: [PATCH 66/90] Added license headers. --- .../naming/ExplicitNamingRequiredTest.scala | 16 +++++++++++ .../implementations/AsIsNamingTest.scala | 16 +++++++++++ .../examples/enceladus/DatasetSchema.scala | 28 +++++++++---------- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/core/src/test/scala/za/co/absa/fadb/naming/ExplicitNamingRequiredTest.scala b/core/src/test/scala/za/co/absa/fadb/naming/ExplicitNamingRequiredTest.scala index 078142fd..8888ba39 100644 --- a/core/src/test/scala/za/co/absa/fadb/naming/ExplicitNamingRequiredTest.scala +++ b/core/src/test/scala/za/co/absa/fadb/naming/ExplicitNamingRequiredTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.naming import org.scalatest.funsuite.AnyFunSuiteLike diff --git a/core/src/test/scala/za/co/absa/fadb/naming/implementations/AsIsNamingTest.scala b/core/src/test/scala/za/co/absa/fadb/naming/implementations/AsIsNamingTest.scala index d7fabc1a..35456d6b 100644 --- a/core/src/test/scala/za/co/absa/fadb/naming/implementations/AsIsNamingTest.scala +++ b/core/src/test/scala/za/co/absa/fadb/naming/implementations/AsIsNamingTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package za.co.absa.fadb.naming.implementations import org.scalatest.funsuite.AnyFunSuiteLike diff --git a/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala b/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala index b0100a31..87eeb81c 100644 --- a/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala +++ b/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala @@ -1,18 +1,18 @@ /* -* Copyright 2022 ABSA Group Limited -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package za.co.absa.fadb.examples.enceladus From bc729eaf1db1cb81f4d51a54aa95a21f02991e7a Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Wed, 27 Dec 2023 18:35:36 +0100 Subject: [PATCH 67/90] Change package name --- .../{doobiedb => doobie}/AvailableMetaInstancesTest.scala | 2 +- .../co/absa/fadb/{doobiedb => doobie}/DatesTimesTest.scala | 4 ++-- .../DoobieMultipleResultFunctionTest.scala | 4 ++-- .../DoobieOptionalResultFunctionTest.scala | 4 ++-- .../fadb/{doobiedb => doobie}/DoobieOtherTypesTest.scala | 4 ++-- .../DoobieSingleResultFunctionTest.scala | 4 ++-- .../DoobieSingleResultFunctionWithStatusTest.scala | 4 ++-- .../za/co/absa/fadb/{doobiedb => doobie}/DoobieTest.scala | 2 +- .../za/co/absa/fadb/{doobiedb => doobie}/DoobieEngine.scala | 2 +- .../co/absa/fadb/{doobiedb => doobie}/DoobieFunction.scala | 6 +++--- .../za/co/absa/fadb/{doobiedb => doobie}/DoobieQuery.scala | 2 +- .../co/absa/fadb/{doobiedb => doobie}/StatusWithData.scala | 2 +- 12 files changed, 20 insertions(+), 20 deletions(-) rename doobie/src/it/scala/za/co/absa/fadb/{doobiedb => doobie}/AvailableMetaInstancesTest.scala (99%) rename doobie/src/it/scala/za/co/absa/fadb/{doobiedb => doobie}/DatesTimesTest.scala (97%) rename doobie/src/it/scala/za/co/absa/fadb/{doobiedb => doobie}/DoobieMultipleResultFunctionTest.scala (93%) rename doobie/src/it/scala/za/co/absa/fadb/{doobiedb => doobie}/DoobieOptionalResultFunctionTest.scala (93%) rename doobie/src/it/scala/za/co/absa/fadb/{doobiedb => doobie}/DoobieOtherTypesTest.scala (96%) rename doobie/src/it/scala/za/co/absa/fadb/{doobiedb => doobie}/DoobieSingleResultFunctionTest.scala (94%) rename doobie/src/it/scala/za/co/absa/fadb/{doobiedb => doobie}/DoobieSingleResultFunctionWithStatusTest.scala (97%) rename doobie/src/it/scala/za/co/absa/fadb/{doobiedb => doobie}/DoobieTest.scala (97%) rename doobie/src/main/scala/za/co/absa/fadb/{doobiedb => doobie}/DoobieEngine.scala (99%) rename doobie/src/main/scala/za/co/absa/fadb/{doobiedb => doobie}/DoobieFunction.scala (97%) rename doobie/src/main/scala/za/co/absa/fadb/{doobiedb => doobie}/DoobieQuery.scala (98%) rename doobie/src/main/scala/za/co/absa/fadb/{doobiedb => doobie}/StatusWithData.scala (95%) diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/AvailableMetaInstancesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/AvailableMetaInstancesTest.scala similarity index 99% rename from doobie/src/it/scala/za/co/absa/fadb/doobiedb/AvailableMetaInstancesTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobie/AvailableMetaInstancesTest.scala index 3d9c1149..53c3f17f 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/AvailableMetaInstancesTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/AvailableMetaInstancesTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie import doobie.util.meta.Meta import org.scalatest.funsuite.AnyFunSuite diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DatesTimesTest.scala similarity index 97% rename from doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobie/DatesTimesTest.scala index bac51165..a0e27db2 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DatesTimesTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DatesTimesTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie import cats.effect.IO import cats.effect.unsafe.implicits.global @@ -23,7 +23,7 @@ import doobie.util.Read import doobie.util.fragment.Fragment import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobiedb.DoobieFunction.{DoobieSingleResultFunction, DoobieSingleResultFunctionWithStatus} +import za.co.absa.fadb.doobie.DoobieFunction.{DoobieSingleResultFunction, DoobieSingleResultFunctionWithStatus} import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling class DatesTimesTest extends AnyFunSuite with DoobieTest { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieMultipleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala similarity index 93% rename from doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieMultipleResultFunctionTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala index 55b3fa02..b208227d 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieMultipleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie import cats.effect.IO import cats.effect.unsafe.implicits.global @@ -23,7 +23,7 @@ import doobie.util.Read import doobie.util.fragment.Fragment import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieMultipleResultFunction +import za.co.absa.fadb.doobie.DoobieFunction.DoobieMultipleResultFunction class DoobieMultipleResultFunctionTest extends AnyFunSuite with DoobieTest { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOptionalResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala similarity index 93% rename from doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOptionalResultFunctionTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala index ac2ddd11..a69928b3 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOptionalResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOptionalResultFunctionTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie import cats.effect.IO import cats.effect.unsafe.implicits.global @@ -23,7 +23,7 @@ import doobie.util.Read import doobie.util.fragment.Fragment import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieOptionalResultFunction +import za.co.absa.fadb.doobie.DoobieFunction.DoobieOptionalResultFunction class DoobieOptionalResultFunctionTest extends AnyFunSuite with DoobieTest { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOtherTypesTest.scala similarity index 96% rename from doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOtherTypesTest.scala index 6681133e..71f1d6c6 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieOtherTypesTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieOtherTypesTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie import cats.effect.IO import cats.effect.unsafe.implicits.global @@ -23,7 +23,7 @@ import doobie.util.Read import doobie.util.fragment.Fragment import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobiedb.DoobieFunction.{DoobieSingleResultFunction, DoobieSingleResultFunctionWithStatus} +import za.co.absa.fadb.doobie.DoobieFunction.{DoobieSingleResultFunction, DoobieSingleResultFunctionWithStatus} import za.co.absa.fadb.exceptions.DataConflictException import za.co.absa.fadb.status.FunctionStatus import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala similarity index 94% rename from doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala index e7a005e1..7454acdc 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie import cats.effect.IO import cats.effect.unsafe.implicits.global @@ -23,7 +23,7 @@ import doobie.util.Read import doobie.util.fragment.Fragment import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieSingleResultFunction +import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunction class DoobieSingleResultFunctionTest extends AnyFunSuite with DoobieTest { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala similarity index 97% rename from doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala index d1fe9bbe..14a98f04 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieSingleResultFunctionWithStatusTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieSingleResultFunctionWithStatusTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie import cats.effect.IO import cats.effect.unsafe.implicits.global @@ -24,7 +24,7 @@ import doobie.util.Read import doobie.util.invariant.NonNullableColumnRead import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieSingleResultFunctionWithStatus +import za.co.absa.fadb.doobie.DoobieFunction.DoobieSingleResultFunctionWithStatus import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling class DoobieSingleResultFunctionWithStatusTest extends AnyFunSuite with DoobieTest { diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala similarity index 97% rename from doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala rename to doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala index a3778c74..36f5c5f9 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobiedb/DoobieTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie import cats.effect.IO import doobie.util.transactor.Transactor diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala similarity index 99% rename from doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala rename to doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala index cf7e105d..d33585b1 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie import cats.effect.Async import cats.implicits._ diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala similarity index 97% rename from doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala rename to doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index d289b97f..14be3b82 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie import cats.Monad import cats.effect.kernel.Async @@ -39,7 +39,7 @@ trait DoobieFunctionBase[R] { * @tparam I the input type of the function * @tparam R the result type of the function */ -private[doobiedb] trait DoobieFunction[I, R] extends DoobieFunctionBase[R] { +private[doobie] trait DoobieFunction[I, R] extends DoobieFunctionBase[R] { /** * Generates a Doobie `Fragment` representing the SQL query for the function. * @@ -57,7 +57,7 @@ private[doobiedb] trait DoobieFunction[I, R] extends DoobieFunctionBase[R] { protected def query(values: I): DoobieQuery[R] = new DoobieQuery[R](sql(values)) } -private[doobiedb] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBase[R] { +private[doobie] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBase[R] { /** * The `Read[StatusWithData[R]]` instance used to read the query result with status into `StatusWithData[R]`. */ diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala similarity index 98% rename from doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieQuery.scala rename to doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala index a398d309..0909d363 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/DoobieQuery.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie import doobie.util.Read import doobie.util.fragment.Fragment diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala similarity index 95% rename from doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala rename to doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala index 88153ae6..fefaebd5 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobiedb/StatusWithData.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/StatusWithData.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie /** * Represents a function status with data. From 4470c43fce1eff4caa49a191bc95783c07e0334f Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Wed, 27 Dec 2023 18:44:21 +0100 Subject: [PATCH 68/90] Change package name --- .../absa/fadb/doobie/DoobieStreamingResultFunctionTest.scala | 4 ++-- .../scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieStreamingResultFunctionTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieStreamingResultFunctionTest.scala index 7560c51c..a3e7f80e 100644 --- a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieStreamingResultFunctionTest.scala +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieStreamingResultFunctionTest.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie import cats.effect.IO import cats.effect.unsafe.implicits.global @@ -23,7 +23,7 @@ import doobie.util.Read import doobie.util.fragment.Fragment import org.scalatest.funsuite.AnyFunSuite import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.doobiedb.DoobieFunction.DoobieStreamingResultFunction +import za.co.absa.fadb.doobie.DoobieFunction.DoobieStreamingResultFunction class DoobieStreamingResultFunctionTest extends AnyFunSuite with DoobieTest { diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala index e4983eb2..176b949e 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package za.co.absa.fadb.doobiedb +package za.co.absa.fadb.doobie import cats.effect.Async import doobie.Transactor From 7fdf6e4aa63eac61d0c233799fbab201dac78839 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Wed, 27 Dec 2023 21:10:48 +0100 Subject: [PATCH 69/90] Change package name --- .../src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index 645428fa..79d014fe 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -25,7 +25,7 @@ import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, DBStreamingFunction, Fun import scala.language.higherKinds -private[doobiedb] trait DoobieFunctionBase[R] { +private[doobie] trait DoobieFunctionBase[R] { /** * The `Read[R]` instance used to read the query result into `R`. */ From e8a5607d9d06939888d0c42b15366521d2e85014 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Wed, 3 Jan 2024 08:37:40 +0100 Subject: [PATCH 70/90] added Doobie into readme files --- README.md | 64 +++++++++++++++++++----------- doobie/{README.md => zio-setup.md} | 0 2 files changed, 41 insertions(+), 23 deletions(-) rename doobie/{README.md => zio-setup.md} (100%) diff --git a/README.md b/README.md index 4bbb3a29..f3622d12 100644 --- a/README.md +++ b/README.md @@ -54,48 +54,50 @@ Currently, the library is developed with Postgres as the target DB. But the appr #### Sbt +Import one of the two available module at the moment. Slick module works with Scala Futures. Doobie module works with any effect type (typically IO or ZIO) provided cats effect's Async instance is available. + ```scala -libraryDependencies ++= Seq( - "za.co.absa.fa-db" %% "core" % "X.Y.Z", - "za.co.absa.fa-db" %% "slick" % "X.Y.Z" -) +libraryDependencies *= "za.co.absa.fa-db" %% "slick" % "X.Y.Z" +libraryDependencies *= "za.co.absa.fa-db" %% "doobie" % "X.Y.Z" ``` #### Maven -##### Scala 2.11 +##### Scala 2.12 Modules: -* Core [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/core_2.11/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/core_2.11) -* Slick [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick_2.11/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick_2.11) +* Core [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/core_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/core_2.12) +* Slick [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick_2.12) +* Doobie [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/doobie_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/doobie_2.12) ```xml - za.co.absa.fa-db - core_2.11 - ${latest_version} + za.co.absa.fa-db + slick_2.12 + ${latest_version} -za.co.absa.fa-db -slick_2.11 -${latest_version} + za.co.absa.fa-db + doobie_2.12 + ${latest_version} ``` -### Scala 2.12 +### Scala 2.13 Modules: -* Core [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/core_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/core_2.12) -* Slick [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick_2.12) +* Core [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/core_2.13/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/core_2.13) +* Slick [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick_2.13/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick_2.13) +* Doobie [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/doobie_2.13/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/doobie_2.13) ```xml - za.co.absa.fa-db - core_2.12 - ${latest_version} + za.co.absa.fa-db + slick_2.13 + ${latest_version} za.co.absa.fa-db - slick_2.12 + doobie_2.13 ${latest_version} ``` @@ -109,13 +111,15 @@ Text about status codes returned from the database function can be found [here]( ## Slick module -Slick module is the first (and so far only) implementation of fa-db able to execute. As the name suggests it runs on -[Slick library](https://github.com/slick/slick) and also brings in the [Slickpg library](https://github.com/tminglei/slick-pg/) for extended Postgres type support. +As the name suggests it runs on [Slick library](https://github.com/slick/slick) and also brings in the [Slickpg library](https://github.com/tminglei/slick-pg/) for extended Postgres type support. It brings: * `class SlickPgEngine` - implementation of _Core_'s `DBEngine` executing the queries via Slick -* `trait SlickFunction` and `trait SlickFunctionWithStatusSupport` - mix-in traits to use with `FaDbFunction` descendants +* `class SlickSingleResultFunction` - abstract class for DB functions returning single result +* `class SlickMultipleResultFunction` - abstract class for DB functions returning sequence of results +* `class SlickOptionalResultFunction` - abstract class for DB functions returning optional result +* `class SlickSingleResultFunctionWithStatus` - abstract class for DB functions with status handling; it requires an implementation of `StatusHandling` to be mixed-in (`StandardStatusHandling` available out-of-the-box) * `trait FaDbPostgresProfile` - to bring support for Postgres and its extended data types in one class (except JSON, as there are multiple implementations for this data type in _Slick-Pg_) * `object FaDbPostgresProfile` - instance of the above trait for direct use @@ -137,6 +141,20 @@ val hStore: Option[Map[String, String]] = pr.nextHStoreOption val macAddr: Option[MacAddrString] = pr.nextMacAddrOption ``` +## Doobie module + +As the name suggests it runs on [Doobie library](https://tpolecat.github.io/doobie/). The main benefit of the module is that it allows to use any effect type (typically IO or ZIO) therefore is more suitable for functional programming. It also brings in the [Doobie-Postgres library](https://tpolecat.github.io/doobie/docs/14-PostgreSQL.html) for extended Postgres type support. + +It brings: + +* `class DoobieEngine` - implementation of _Core_'s `DBEngine` executing the queries via Doobie. The class is type parameterized with the effect type. +* `class DoobieSingleResultFunction` - abstract class for DB functions returning single result +* `class DoobieMultipleResultFunction` - abstract class for DB functions returning sequence of results +* `class DoobieOptionalResultFunction` - abstract class for DB functions returning optional result +* `class DoobieSingleResultFunctionWithStatus` - abstract class for DB functions with status handling; it requires an implementation of `StatusHandling` to be mixed-in (`StandardStatusHandling` available out-of-the-box) + +Since Doobie also interoperates with ZIO, there is an example of how a database connection can be properly established within a ZIO application. Please see [this file](doobie/zio-setup.md) for more details. + ## Testing ### How to generate unit tests code coverage report diff --git a/doobie/README.md b/doobie/zio-setup.md similarity index 100% rename from doobie/README.md rename to doobie/zio-setup.md From 0268e9a77d538eaebb5f2e2fb7720c11590a0942 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Wed, 3 Jan 2024 08:55:20 +0100 Subject: [PATCH 71/90] merged with parent branch, updated docs --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f3622d12..e55187d5 100644 --- a/README.md +++ b/README.md @@ -116,10 +116,12 @@ As the name suggests it runs on [Slick library](https://github.com/slick/slick) It brings: * `class SlickPgEngine` - implementation of _Core_'s `DBEngine` executing the queries via Slick +* `class SlickPgStreamingEngine` - implementation of _Core_'s `DBStreamingEngine` executing the queries via Slick in a streaming fashion * `class SlickSingleResultFunction` - abstract class for DB functions returning single result * `class SlickMultipleResultFunction` - abstract class for DB functions returning sequence of results * `class SlickOptionalResultFunction` - abstract class for DB functions returning optional result * `class SlickSingleResultFunctionWithStatus` - abstract class for DB functions with status handling; it requires an implementation of `StatusHandling` to be mixed-in (`StandardStatusHandling` available out-of-the-box) +* `class SlickStreamingResultFunction` - abstract class for DB functions returning sequence of results (fs2.Stream) in a streaming fashion * `trait FaDbPostgresProfile` - to bring support for Postgres and its extended data types in one class (except JSON, as there are multiple implementations for this data type in _Slick-Pg_) * `object FaDbPostgresProfile` - instance of the above trait for direct use @@ -148,11 +150,12 @@ As the name suggests it runs on [Doobie library](https://tpolecat.github.io/doob It brings: * `class DoobieEngine` - implementation of _Core_'s `DBEngine` executing the queries via Doobie. The class is type parameterized with the effect type. +* `class DoobieStreamingEngine` - implementation of _Core_'s `DBStreamingEngine` executing the queries via Doobie in a streaming fashion. The class is type parameterized with the effect type. * `class DoobieSingleResultFunction` - abstract class for DB functions returning single result * `class DoobieMultipleResultFunction` - abstract class for DB functions returning sequence of results * `class DoobieOptionalResultFunction` - abstract class for DB functions returning optional result * `class DoobieSingleResultFunctionWithStatus` - abstract class for DB functions with status handling; it requires an implementation of `StatusHandling` to be mixed-in (`StandardStatusHandling` available out-of-the-box) - +* `class DoobieStreamingResultFunction` - abstract class for DB functions returning sequence of results (fs2.Stream) in a streaming fashion Since Doobie also interoperates with ZIO, there is an example of how a database connection can be properly established within a ZIO application. Please see [this file](doobie/zio-setup.md) for more details. ## Testing From 0a58735e33da5ef50e649b81473d543e5a31c997 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Wed, 3 Jan 2024 11:53:42 +0100 Subject: [PATCH 72/90] DBFunction made public --- core/src/main/scala/za/co/absa/fadb/DBFunction.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index be3a11b1..4517c65e 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -31,7 +31,7 @@ import scala.language.higherKinds * @tparam E - The type of the [[DBEngine]] engine. * @tparam F - The type of the context in which the database function is executed. */ -private[fadb] abstract class DBFunction[I, R, E <: DBEngine[F], F[_]](functionNameOverride: Option[String] = None)( +abstract class DBFunction[I, R, E <: DBEngine[F], F[_]](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, val dBEngine: E ) extends DBFunctionFabric(functionNameOverride) { From ad36f5f6936fb85563a56b76c7f1d031792b01f5 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Wed, 3 Jan 2024 21:51:53 +0100 Subject: [PATCH 73/90] streaming and slick-streaming modules --- build.sbt | 32 ++++++++- .../scala/za/co/absa/fadb/DBFunction.scala | 55 --------------- .../co/absa/fadb/doobie/DoobieFunction.scala | 6 +- .../fadb/doobie/DoobieStreamingEngine.scala | 2 +- project/Dependencies.scala | 14 +++- .../SlickStreamingResultFunctionTest.scala | 4 +- .../streaming}/SlickPgStreamingEngine.scala | 5 +- .../SlickStreamingResultFunction.scala | 33 +++++++++ .../za/co/absa/fadb/slick/SlickFunction.scala | 16 +---- .../fadb/streaming}/DBStreamingEngine.scala | 4 +- .../fadb/streaming/DBStreamingFunction.scala | 69 +++++++++++++++++++ 11 files changed, 155 insertions(+), 85 deletions(-) rename {slick/src/it/scala/za/co/absa/fadb/slick => slick-streaming/src/it/scala/za/co/absa/fadb/slick/streaming}/SlickStreamingResultFunctionTest.scala (94%) rename {slick/src/main/scala/za/co/absa/fadb/slick => slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming}/SlickPgStreamingEngine.scala (94%) create mode 100644 slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickStreamingResultFunction.scala rename {core/src/main/scala/za/co/absa/fadb => streaming/src/main/scala/za/co/absa/fadb/streaming}/DBStreamingEngine.scala (96%) create mode 100644 streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingFunction.scala diff --git a/build.sbt b/build.sbt index 494eb38e..c7d8ca59 100644 --- a/build.sbt +++ b/build.sbt @@ -73,6 +73,21 @@ lazy val faDbCore = (project in file("core")) jacocoExcludes := commonJacocoExcludes ) +lazy val faDBStreaming = (project in file("streaming")) + .configs(IntegrationTest) + .settings( + name := "streaming", + libraryDependencies ++= streamingDependencies(scalaVersion.value), + javacOptions ++= commonJavacOptions, + scalacOptions ++= commonScalacOptions, + (Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, // printScalaVersion is run with compile + Defaults.itSettings, + ).dependsOn(faDbCore) + .settings( + jacocoReportSettings := commonJacocoReportSettings.withTitle(s"fa-db:streaming Jacoco Report - scala:${scalaVersion.value}"), + jacocoExcludes := commonJacocoExcludes + ) + lazy val faDBSlick = (project in file("slick")) .configs(IntegrationTest) .settings( @@ -88,6 +103,21 @@ lazy val faDBSlick = (project in file("slick")) jacocoExcludes := commonJacocoExcludes ) +lazy val faDBSlickStreaming = (project in file("slick-streaming")) + .configs(IntegrationTest) + .settings( + name := "slick-streaming", + libraryDependencies ++= slickStreamingDependencies(scalaVersion.value), + javacOptions ++= commonJavacOptions, + scalacOptions ++= commonScalacOptions, + (Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, // printScalaVersion is run with compile + Defaults.itSettings, + ).dependsOn(faDbCore, faDBStreaming, faDBSlick) + .settings( + jacocoReportSettings := commonJacocoReportSettings.withTitle(s"fa-db:slick-streaming Jacoco Report - scala:${scalaVersion.value}"), + jacocoExcludes := commonJacocoExcludes + ) + lazy val faDBDoobie = (project in file("doobie")) .configs(IntegrationTest) .settings( @@ -96,7 +126,7 @@ lazy val faDBDoobie = (project in file("doobie")) javacOptions ++= commonJavacOptions, scalacOptions ++= commonScalacOptions, Defaults.itSettings, - ).dependsOn(faDbCore) + ).dependsOn(faDbCore, faDBStreaming) .settings( jacocoReportSettings := commonJacocoReportSettings.withTitle(s"fa-db:doobie Jacoco Report - scala:${scalaVersion.value}"), jacocoExcludes := commonJacocoExcludes diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 447c1b6d..ad8da2df 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -123,66 +123,11 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]](functionNameOv * @return the SQL query in the format specific to the provided [[DBEngine]] */ protected def query(values: I): dBEngine.QueryWithStatusType[R] - * Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the - * DBEngine specific mix-in. - * @param values the values to pass over to the database function - * @return the SQL query in the format specific to the provided [[DBEngine]] - */ - protected def query(values: I): dBEngine.QueryWithStatusType[R] // To be provided by an implementation of QueryStatusHandling override def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] } -/** - * `DBStreamingFunction` is an abstract class that represents a database function returning a stream of results. - * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. - * @param schema - The schema the function belongs to. - * @param dbStreamingEngine - The database engine that is supposed to execute the function (contains connection to the database). - * @tparam I - The type covering the input fields of the database function. - * @tparam R - The type covering the returned fields from the database function. - * @tparam E - The type of the [[DBStreamingEngine]] engine. - * @tparam F - The type of the context in which the database function is executed. - */ -abstract class DBStreamingFunction[I, R, E <: DBStreamingEngine[F], F[_]](functionNameOverride: Option[String] = None)( - implicit override val schema: DBSchema, - val dbStreamingEngine: E -) extends DBFunctionFabric(functionNameOverride) { - - // A constructor that takes only the mandatory parameters and uses default values for the optional ones - def this()(implicit schema: DBSchema, dBEngine: E) = this(None) - - // A constructor that allows specifying the function name as a string, but not as an option - def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) - - /** - * Function to create the DB function call specific to the provided [[DBEngine]]. - * Expected to be implemented by the DBEngine specific mix-in. - * @param values - The values to pass over to the database function. - * @return - The SQL query in the format specific to the provided [[DBEngine]]. - */ - protected def query(values: I): dbStreamingEngine.QueryType[R] - - // To be provided by an implementation of QueryStatusHandling - override def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] - /** - * Executes the database function and returns stream of results - * @param values The values to pass over to the database function - * @return A stream of results from the database function - */ - def apply(values: I): fs2.Stream[F, R] = dbStreamingEngine.runStreaming(query(values)) - - /** - * Executes the database function and returns stream of results. Allows to specify chunk size. - * @param values The values to pass over to the database function - * @param chunkSize The chunk size to use for the stream - * @return A stream of results from the database function - */ - def apply(values: I, chunkSize: Int): fs2.Stream[F, R] = { - dbStreamingEngine.runStreamingWithChunkSize(query(values), chunkSize) - } -} - object DBFunction { /** diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index e2de7efe..41ce7516 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -21,11 +21,7 @@ import doobie.util.Read import doobie.util.fragment.Fragment import za.co.absa.fadb.DBFunction._ import za.co.absa.fadb.exceptions.StatusException -import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, DBStreamingFunction, FunctionStatusWithData} - -import scala.language.higherKinds - -private[doobie] trait DoobieFunctionBase[R] { +import za.co.absa.fadb.streaming.DBStreamingFunction import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, FunctionStatusWithData} import scala.language.higherKinds diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala index 176b949e..4eca0785 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala @@ -20,7 +20,7 @@ import cats.effect.Async import doobie.Transactor import doobie.implicits.toDoobieStreamOps import doobie.util.Read -import za.co.absa.fadb.DBStreamingEngine +import za.co.absa.fadb.streaming.DBStreamingEngine import scala.language.higherKinds diff --git a/project/Dependencies.scala b/project/Dependencies.scala index d4788ca7..bbd7d8d0 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -18,11 +18,8 @@ import sbt._ object Dependencies { - private def commonDependencies(scalaVersion: String): Seq[ModuleID] = Seq( "org.typelevel" %% "cats-core" % "2.9.0", - "org.typelevel" %% "cats-effect" % "3.5.0", - "co.fs2" %% "fs2-core" % "3.7.0", "org.scalatest" %% "scalatest" % "3.1.0" % "test,it", "org.scalatest" %% "scalatest-flatspec" % "3.2.0" % "test,it", "org.scalatestplus" %% "mockito-1-10" % "3.1.0.0" % "test,it" @@ -35,6 +32,12 @@ object Dependencies { ) } + def streamingDependencies(scalaVersion: String): Seq[ModuleID] = { + commonDependencies(scalaVersion) ++ Seq( + "co.fs2" %% "fs2-core" % "3.7.0", + ) + } + def slickDependencies(scalaVersion: String): Seq[ModuleID] = { commonDependencies(scalaVersion) ++ Seq( "com.typesafe.slick" %% "slick" % "3.3.3", @@ -42,6 +45,11 @@ object Dependencies { "com.typesafe.slick" %% "slick-hikaricp" % "3.3.3", "org.postgresql" % "postgresql" % "42.6.0", "com.github.tminglei" %% "slick-pg" % "0.20.4" % Optional, + ) + } + + def slickStreamingDependencies(scalaVersion: String): Seq[ModuleID] = { + commonDependencies(scalaVersion) ++ slickDependencies(scalaVersion) ++ streamingDependencies(scalaVersion) ++ Seq( "co.fs2" %% "fs2-reactive-streams" % "3.9.3" ) } diff --git a/slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala b/slick-streaming/src/it/scala/za/co/absa/fadb/slick/streaming/SlickStreamingResultFunctionTest.scala similarity index 94% rename from slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala rename to slick-streaming/src/it/scala/za/co/absa/fadb/slick/streaming/SlickStreamingResultFunctionTest.scala index cd4044d7..676a13a8 100644 --- a/slick/src/it/scala/za/co/absa/fadb/slick/SlickStreamingResultFunctionTest.scala +++ b/slick-streaming/src/it/scala/za/co/absa/fadb/slick/streaming/SlickStreamingResultFunctionTest.scala @@ -14,15 +14,15 @@ * limitations under the License. */ -package za.co.absa.fadb.slick +package za.co.absa.fadb.slick.streaming import cats.effect.IO import cats.effect.unsafe.implicits.global import org.scalatest.funsuite.AnyFunSuite import slick.jdbc.SQLActionBuilder import za.co.absa.fadb.DBSchema -import za.co.absa.fadb.slick.SlickFunction.SlickStreamingResultFunction import za.co.absa.fadb.slick.FaDbPostgresProfile.api._ +import za.co.absa.fadb.slick.{Actor, ActorSlickConverter, SlickTest} class SlickStreamingResultFunctionTest extends AnyFunSuite with SlickTest { diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala b/slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickPgStreamingEngine.scala similarity index 94% rename from slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala rename to slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickPgStreamingEngine.scala index f7d7cff5..1ba223ef 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgStreamingEngine.scala +++ b/slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickPgStreamingEngine.scala @@ -14,12 +14,13 @@ * limitations under the License. */ -package za.co.absa.fadb.slick +package za.co.absa.fadb.slick.streaming import cats.effect.Async import fs2.interop.reactivestreams.PublisherOps import slick.jdbc.JdbcBackend.Database -import za.co.absa.fadb.DBStreamingEngine +import za.co.absa.fadb.slick.SlickQuery +import za.co.absa.fadb.streaming.DBStreamingEngine import scala.language.higherKinds diff --git a/slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickStreamingResultFunction.scala b/slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickStreamingResultFunction.scala new file mode 100644 index 00000000..d081d66e --- /dev/null +++ b/slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickStreamingResultFunction.scala @@ -0,0 +1,33 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.fadb.slick.streaming + +import cats.effect.Async +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.slick.SlickFunction +import za.co.absa.fadb.streaming.DBStreamingFunction + +import scala.language.higherKinds + +/** + * Class for Slick DB functions with streaming results. + */ +abstract class SlickStreamingResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)(implicit + override val schema: DBSchema, + dBEngine: SlickPgStreamingEngine[F] +) extends DBStreamingFunction[I, R, SlickPgStreamingEngine[F], F](functionNameOverride) + with SlickFunction[I, R] diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala index 91c2a513..2242bf2b 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala @@ -16,17 +16,12 @@ package za.co.absa.fadb.slick -import cats.effect.kernel.Async import slick.jdbc.{GetResult, SQLActionBuilder} import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBOptionalResultFunction, DBSingleResultFunction} import za.co.absa.fadb.exceptions.StatusException import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, FunctionStatusWithData} -import za.co.absa.fadb.DBFunction.{DBMultipleResultFunction, DBOptionalResultFunction, DBSingleResultFunction} -import za.co.absa.fadb.exceptions.StatusException -import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, DBStreamingFunction, FunctionStatusWithData} import scala.concurrent.Future - import scala.language.higherKinds /** @@ -53,7 +48,7 @@ private[slick] trait SlickFunctionBase[I, R] { def fieldsToSelect: Seq[String] } -private[slick] trait SlickFunction[I, R] extends SlickFunctionBase[I, R] { +trait SlickFunction[I, R] extends SlickFunctionBase[I, R] { /** * Generates a `SlickQuery[R]` representing the SQL query for the function. @@ -108,15 +103,6 @@ object SlickFunction { ) extends DBMultipleResultFunction[I, R, SlickPgEngine, Future](functionNameOverride) with SlickFunction[I, R] - /** - * Class for Slick DB functions with streaming results. - */ - abstract class SlickStreamingResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)(implicit - override val schema: DBSchema, - dBEngine: SlickPgStreamingEngine[F] - ) extends DBStreamingFunction[I, R, SlickPgStreamingEngine[F], F](functionNameOverride) - with SlickFunction[I, R] - /** * Class for Slick DB functions with optional result. */ diff --git a/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala b/streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingEngine.scala similarity index 96% rename from core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala rename to streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingEngine.scala index a265fa90..2d1b1aaf 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBStreamingEngine.scala +++ b/streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingEngine.scala @@ -14,7 +14,9 @@ * limitations under the License. */ -package za.co.absa.fadb +package za.co.absa.fadb.streaming + +import za.co.absa.fadb.Query import scala.language.higherKinds diff --git a/streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingFunction.scala b/streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingFunction.scala new file mode 100644 index 00000000..6d164c3b --- /dev/null +++ b/streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingFunction.scala @@ -0,0 +1,69 @@ +/* + * Copyright 2022 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.fadb.streaming + +import za.co.absa.fadb.{DBEngine, DBFunctionFabric, DBSchema} + +import scala.language.higherKinds + +/** + * `DBStreamingFunction` is an abstract class that represents a database function returning a stream of results. + * + * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. + * @param schema - The schema the function belongs to. + * @param dbStreamingEngine - The database engine that is supposed to execute the function (contains connection to the database). + * @tparam I - The type covering the input fields of the database function. + * @tparam R - The type covering the returned fields from the database function. + * @tparam E - The type of the [[DBStreamingEngine]] engine. + * @tparam F - The type of the context in which the database function is executed. + */ +abstract class DBStreamingFunction[I, R, E <: DBStreamingEngine[F], F[_]](functionNameOverride: Option[String] = None)( + implicit override val schema: DBSchema, + val dbStreamingEngine: E +) extends DBFunctionFabric(functionNameOverride) { + + // A constructor that takes only the mandatory parameters and uses default values for the optional ones + def this()(implicit schema: DBSchema, dBEngine: E) = this(None) + + // A constructor that allows specifying the function name as a string, but not as an option + def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName)) + + /** + * Function to create the DB function call specific to the provided [[DBEngine]]. + * Expected to be implemented by the DBEngine specific mix-in. + * @param values - The values to pass over to the database function. + * @return - The SQL query in the format specific to the provided [[DBEngine]]. + */ + protected def query(values: I): dbStreamingEngine.QueryType[R] + + /** + * Executes the database function and returns stream of results + * @param values The values to pass over to the database function + * @return A stream of results from the database function + */ + def apply(values: I): fs2.Stream[F, R] = dbStreamingEngine.runStreaming(query(values)) + + /** + * Executes the database function and returns stream of results. Allows to specify chunk size. + * @param values The values to pass over to the database function + * @param chunkSize The chunk size to use for the stream + * @return A stream of results from the database function + */ + def apply(values: I, chunkSize: Int): fs2.Stream[F, R] = { + dbStreamingEngine.runStreamingWithChunkSize(query(values), chunkSize) + } +} From 5e4561969883d9a18705584a04b740b07bbf636d Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Wed, 3 Jan 2024 21:58:57 +0100 Subject: [PATCH 74/90] jacoco for new modules --- .github/workflows/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 113e0e2c..3d91d546 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,8 +51,10 @@ jobs: with: path: | ${{ github.workspace }}/core/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml + ${{ github.workspace }}/streaming/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml ${{ github.workspace }}/examples/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml ${{ github.workspace }}/slick/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml + ${{ github.workspace }}/slick-streaming/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml ${{ github.workspace }}/doobie/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml key: ${{ runner.os }}-${{ matrix.scalaShort }}-${{ hashFiles('**/jacoco.xml') }} @@ -79,8 +81,10 @@ jobs: with: path: | ${{ github.workspace }}/core/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml + ${{ github.workspace }}/streaming/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml ${{ github.workspace }}/examples/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml ${{ github.workspace }}/slick/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml + ${{ github.workspace }}/slick-streaming/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml ${{ github.workspace }}/doobie/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml key: ${{ runner.os }}-${{ matrix.scalaShort }}-${{ hashFiles('**/jacoco.xml') }} - name: Setup Scala @@ -93,7 +97,9 @@ jobs: with: paths: > ${{ github.workspace }}/core/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml, + ${{ github.workspace }}/streaming/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml, ${{ github.workspace }}/slick/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml + ${{ github.workspace }}/slick-streaming/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml ${{ github.workspace }}/doobie/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml # examples don't need code coverage - at least not now token: ${{ secrets.GITHUB_TOKEN }} From cb1fe0e57cff0d2e4d2b61bdee01460b7e5d3d3a Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Wed, 3 Jan 2024 22:08:18 +0100 Subject: [PATCH 75/90] minors --- core/src/main/scala/za/co/absa/fadb/DBFunction.scala | 2 +- slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index ad8da2df..85782a05 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -100,7 +100,7 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]](functionNameOv /** * Executes the database function and returns multiple results. - * @param values + * @param values the values to pass over to the database function. * @return A sequence of results from the database function. */ def apply(values: I): F[Either[StatusException, R]] = dBEngine.runWithStatus(query(values)) diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala index 2242bf2b..ade087c9 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala @@ -22,7 +22,6 @@ import za.co.absa.fadb.exceptions.StatusException import za.co.absa.fadb.{DBFunctionWithStatus, DBSchema, FunctionStatusWithData} import scala.concurrent.Future -import scala.language.higherKinds /** * Base class for Slick DB functions. From fb92e8398e99a72b75778a97e01a9e6e014a6e3a Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 08:44:00 +0100 Subject: [PATCH 76/90] backtics in docs replaced by square brackets in core module --- core/src/main/scala/za/co/absa/fadb/DBEngine.scala | 2 +- core/src/main/scala/za/co/absa/fadb/DBFunction.scala | 10 +++++----- core/src/main/scala/za/co/absa/fadb/DBSchema.scala | 6 +++--- .../co/absa/fadb/naming/ExplicitNamingRequired.scala | 12 ++++++------ .../scala/za/co/absa/fadb/naming/LettersCase.scala | 8 ++++---- .../za/co/absa/fadb/naming/NamingConvention.scala | 2 +- .../fadb/naming/implementations/AsIsNaming.scala | 5 +++-- .../naming/implementations/SnakeCaseNaming.scala | 4 ++-- .../absa/fadb/status/handling/StatusHandling.scala | 4 ++-- .../implementations/StandardStatusHandling.scala | 2 +- 10 files changed, 28 insertions(+), 27 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala index d282749d..a81eb9a7 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBEngine.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBEngine.scala @@ -23,7 +23,7 @@ import za.co.absa.fadb.exceptions.StatusException import scala.language.higherKinds /** - * `DBEngine` is an abstract class that represents a database engine. + * [[DBEngine]] is an abstract class that represents a database engine. * It provides methods to execute queries and fetch results from a database. * @tparam F - The type of the context in which the database queries are executed. */ diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala index 85782a05..7dafb018 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -22,7 +22,7 @@ import za.co.absa.fadb.status.handling.StatusHandling import scala.language.higherKinds /** - * `DBFunction` is an abstract class that represents a database function. + * [[DBFunction]] is an abstract class that represents a database function. * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. * @param schema - The schema the function belongs to. * @param dBEngine - The database engine that is supposed to execute the function (contains connection to the database). @@ -73,7 +73,7 @@ abstract class DBFunction[I, R, E <: DBEngine[F], F[_]](functionNameOverride: Op } /** - * `DBFunctionWithStatus` is an abstract class that represents a database function with a status. + * [[DBFunctionWithStatus]] is an abstract class that represents a database function with a status. * It extends the [[DBFunction]] class and adds handling for the status of the function invocation. * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. * @param schema - The schema the function belongs to. @@ -131,7 +131,7 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]](functionNameOv object DBFunction { /** - * `DBMultipleResultFunction` is an abstract class that represents a database function returning multiple results. + * [[DBMultipleResultFunction]] is an abstract class that represents a database function returning multiple results. * It extends the [[DBFunction]] class and overrides the apply method to return a sequence of results. */ abstract class DBMultipleResultFunction[I, R, E <: DBEngine[F], F[_]]( @@ -155,7 +155,7 @@ object DBFunction { } /** - * `DBSingleResultFunction` is an abstract class that represents a database function returning a single result. + * [[DBSingleResultFunction]] is an abstract class that represents a database function returning a single result. * It extends the [[DBFunction]] class and overrides the apply method to return a single result. */ abstract class DBSingleResultFunction[I, R, E <: DBEngine[F], F[_]]( @@ -178,7 +178,7 @@ object DBFunction { } /** - * `DBOptionalResultFunction` is an abstract class that represents a database function returning an optional result. + * [[DBOptionalResultFunction]] is an abstract class that represents a database function returning an optional result. * It extends the [[DBFunction]] class and overrides the apply method to return an optional result. */ abstract class DBOptionalResultFunction[I, R, E <: DBEngine[F], F[_]]( diff --git a/core/src/main/scala/za/co/absa/fadb/DBSchema.scala b/core/src/main/scala/za/co/absa/fadb/DBSchema.scala index 4f119a11..b23bb613 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBSchema.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBSchema.scala @@ -22,7 +22,7 @@ import za.co.absa.fadb.naming.NamingConvention * An abstract class, an ancestor to represent a database schema * The database name of the schema is derived from the class name based on the provided naming convention * @param schemaNameOverride - in case the class name would not match the database schema name, this gives the - * @param namingConvention - the [[za.co.absa.fadb.naming.NamingConvention NamingConvention]] + * @param namingConvention - the [[za.co.absa.fadb.naming.NamingConvention]] * prescribing how to convert a class name into a db object name */ abstract class DBSchema(schemaNameOverride: Option[String] = None)(implicit val namingConvention: NamingConvention) { @@ -40,13 +40,13 @@ abstract class DBSchema(schemaNameOverride: Option[String] = None)(implicit val } /** - * To easy pass over to `DBFunction` members of the schema + * To easy pass over to [[DBFunction]] members of the schema */ protected implicit val schema: DBSchema = this /** * Function to convert a class to the associated DB object name, based on the class' name. For transformation from the - * class name to usual db name the schema's [[za.co.absa.fadb.naming.NamingConvention NamingConvention]] is used. + * class name to usual db name the schema's [[za.co.absa.fadb.naming.NamingConvention]] is used. * @param c - class which name to use to get the DB object name * @return - the db object name */ diff --git a/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala b/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala index b0fbde12..962bba4e 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala @@ -19,15 +19,15 @@ package za.co.absa.fadb.naming import za.co.absa.fadb.exceptions.NamingException /** - * `ExplicitNamingRequired` is a `NamingConvention` that throws a `NamingConvention` for any string. + * [[ExplicitNamingRequired]] is a [[NamingConvention]] that throws a [[NamingException]] for any string. * This is used when explicit naming is required and no other naming convention should be applied. */ class ExplicitNamingRequired extends NamingConvention { /** - * Throws a `NamingConvention` with a message indicating that explicit naming is required. + * Throws a [[NamingException]] with a message indicating that explicit naming is required. * @param original - The original string. - * @return Nothing, as a `NamingException` is always thrown. + * @return Nothing, as a [[NamingException]] is always thrown. */ override def stringPerConvention(original: String): String = { val message = s"No convention for '$original', explicit naming required." @@ -36,14 +36,14 @@ class ExplicitNamingRequired extends NamingConvention { } /** - * `ExplicitNamingRequired.Implicits` provides an implicit `NamingConvention` instance that - * throws a `NamingException` for any string. + * [[ExplicitNamingRequired.Implicits]] provides an implicit [[NamingConvention]] instance that + * throws a [[NamingException]] for any string. */ object ExplicitNamingRequired { object Implicits { /** - * An implicit `NamingConvention` instance that throws a `NamingException` for any string. + * An implicit [[NamingConvention]] instance that throws a [[NamingException]] for any string. */ implicit val namingConvention: NamingConvention = new ExplicitNamingRequired() } diff --git a/core/src/main/scala/za/co/absa/fadb/naming/LettersCase.scala b/core/src/main/scala/za/co/absa/fadb/naming/LettersCase.scala index 471dc1c1..6d194077 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/LettersCase.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/LettersCase.scala @@ -17,7 +17,7 @@ package za.co.absa.fadb.naming /** - * `LettersCase` is a sealed trait that represents different cases of letters. + * [[LettersCase]] is a sealed trait that represents different cases of letters. * It provides a method to convert a string to the specific case. */ sealed trait LettersCase { @@ -33,21 +33,21 @@ sealed trait LettersCase { object LettersCase { /** - * `AsIs` is a [[LettersCase]] that leaves strings as they are. + * [[AsIs]] is a [[LettersCase]] that leaves strings as they are. */ case object AsIs extends LettersCase { override def convert(s: String): String = s } /** - * `LowerCase` is a [[LettersCase]] that converts strings to lower case. + * [[LowerCase]] is a [[LettersCase]] that converts strings to lower case. */ case object LowerCase extends LettersCase { override def convert(s: String): String = s.toLowerCase } /** - * `UpperCase` is a [[LettersCase]] that converts strings to upper case. + * [[UpperCase]] is a [[LettersCase]] that converts strings to upper case. */ case object UpperCase extends LettersCase { override def convert(s: String): String = s.toUpperCase diff --git a/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala b/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala index b5adadfa..e082a0c9 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/NamingConvention.scala @@ -17,7 +17,7 @@ package za.co.absa.fadb.naming /** - * `NamingConvention` is a base trait that defines the interface for different naming conventions. + * [[NamingConvention]] is a base trait that defines the interface for different naming conventions. * It provides methods to convert a class name according to given naming convention. */ trait NamingConvention { diff --git a/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala b/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala index 926cd00c..3890cac7 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/implementations/AsIsNaming.scala @@ -20,8 +20,9 @@ import za.co.absa.fadb.naming.{LettersCase, NamingConvention} import LettersCase.AsIs /** - * `AsIsNaming` provides a naming convention that leaves strings as they are. + * [[AsIsNaming]] provides a naming convention that leaves strings as they are. * It implements the [[NamingConvention]] trait. + * * @param lettersCase - The case of the letters in the string. */ class AsIsNaming(lettersCase: LettersCase) extends NamingConvention { @@ -37,7 +38,7 @@ class AsIsNaming(lettersCase: LettersCase) extends NamingConvention { } /** - * `AsIsNaming.Implicits` provides an implicit [[NamingConvention]] instance that leaves strings as they are. + * [[AsIsNaming.Implicits]] provides an implicit [[NamingConvention]] instance that leaves strings as they are. */ object AsIsNaming { object Implicits { diff --git a/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala b/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala index 7362ea2c..caa96fda 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/implementations/SnakeCaseNaming.scala @@ -20,7 +20,7 @@ import za.co.absa.fadb.naming.{LettersCase, NamingConvention} import LettersCase.LowerCase /** - * `SnakeCaseNaming` provides a naming convention that converts camel case strings to snake case. + * [[SnakeCaseNaming]] provides a naming convention that converts camel case strings to snake case. * It implements the [[NamingConvention]] trait. * @param lettersCase - The case of the letters in the string. */ @@ -51,7 +51,7 @@ class SnakeCaseNaming(lettersCase: LettersCase) extends NamingConvention { } /** - * `SnakeCaseNaming.Implicits` provides an implicit [[NamingConvention]] instance that converts camel case strings to snake case. + * [[SnakeCaseNaming.Implicits]] provides an implicit [[NamingConvention]] instance that converts camel case strings to snake case. */ object SnakeCaseNaming { object Implicits { diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala index 82768ac5..f93f3eeb 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala @@ -20,7 +20,7 @@ import za.co.absa.fadb.FunctionStatusWithData import za.co.absa.fadb.exceptions.StatusException /** - * `StatusHandling` is a base trait that defines the interface for handling the status of a function invocation. + * [[StatusHandling]] is a base trait that defines the interface for handling the status of a function invocation. * It provides a method to check the status of a function invocation with data. */ trait StatusHandling { @@ -28,7 +28,7 @@ trait StatusHandling { /** * Checks the status of a function invocation. * @param statusWithData - The status of the function invocation with data. - * @return Either a `StatusException` if the status code indicates an error, or the data if the status code is successful. + * @return Either a [[StatusException]] if the status code indicates an error, or the data if the status code is successful. */ def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] } diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala index 2e491082..2fa0176e 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/implementations/StandardStatusHandling.scala @@ -21,7 +21,7 @@ import za.co.absa.fadb.exceptions._ import za.co.absa.fadb.status.handling.StatusHandling /** - * `StandardStatusHandling` is a trait that extends the `StatusHandling` interface. + * [[StandardStatusHandling]] is a trait that implements the [[StatusHandling]] interface. * It provides a standard implementation for checking the status of a function invocation. */ trait StandardStatusHandling extends StatusHandling { From 64dbe9005b356c57ddbcc252940de5abb6be3afb Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 08:47:57 +0100 Subject: [PATCH 77/90] backtics in docs replaced by square brackets in core module --- .../co/absa/fadb/naming/ExplicitNamingRequired.scala | 10 +++++----- .../scala/za/co/absa/fadb/doobie/DoobieEngine.scala | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala b/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala index 962bba4e..b136e321 100644 --- a/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala +++ b/core/src/main/scala/za/co/absa/fadb/naming/ExplicitNamingRequired.scala @@ -19,15 +19,15 @@ package za.co.absa.fadb.naming import za.co.absa.fadb.exceptions.NamingException /** - * [[ExplicitNamingRequired]] is a [[NamingConvention]] that throws a [[NamingException]] for any string. + * [[ExplicitNamingRequired]] is a [[NamingConvention]] that throws a [[za.co.absa.fadb.exceptions.NamingException]] for any string. * This is used when explicit naming is required and no other naming convention should be applied. */ class ExplicitNamingRequired extends NamingConvention { /** - * Throws a [[NamingException]] with a message indicating that explicit naming is required. + * Throws a [[za.co.absa.fadb.exceptions.NamingException]] with a message indicating that explicit naming is required. * @param original - The original string. - * @return Nothing, as a [[NamingException]] is always thrown. + * @return Nothing, as a [[za.co.absa.fadb.exceptions.NamingException]] is always thrown. */ override def stringPerConvention(original: String): String = { val message = s"No convention for '$original', explicit naming required." @@ -37,13 +37,13 @@ class ExplicitNamingRequired extends NamingConvention { /** * [[ExplicitNamingRequired.Implicits]] provides an implicit [[NamingConvention]] instance that - * throws a [[NamingException]] for any string. + * throws a [[za.co.absa.fadb.exceptions.NamingException]] for any string. */ object ExplicitNamingRequired { object Implicits { /** - * An implicit [[NamingConvention]] instance that throws a [[NamingException]] for any string. + * An implicit [[NamingConvention]] instance that throws a [[za.co.absa.fadb.exceptions.NamingException]] for any string. */ implicit val namingConvention: NamingConvention = new ExplicitNamingRequired() } diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala index d33585b1..25ae0df3 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala @@ -27,7 +27,7 @@ import za.co.absa.fadb.exceptions.StatusException import scala.language.higherKinds /** - * `DoobieEngine` is a class that extends `DBEngine` with `F` as the effect type. + * [[DoobieEngine]] is a class that extends `DBEngine` with `F` as the effect type. * It uses Doobie's `Transactor[F]` to execute SQL queries. * * `Async` is needed because Doobie requires it for non-blocking database operations. From 05abfd48484ec7a06617417b36207773719308d5 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 08:50:16 +0100 Subject: [PATCH 78/90] docs fix --- .../scala/za/co/absa/fadb/status/handling/StatusHandling.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala index f93f3eeb..75d784bd 100644 --- a/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/status/handling/StatusHandling.scala @@ -28,7 +28,7 @@ trait StatusHandling { /** * Checks the status of a function invocation. * @param statusWithData - The status of the function invocation with data. - * @return Either a [[StatusException]] if the status code indicates an error, or the data if the status code is successful. + * @return Either a [[za.co.absa.fadb.exceptions.StatusException]] if the status code indicates an error, or the data if the status code is successful. */ def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A] } From 6b760042081e701dd11ae721975d775944153e4e Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 08:57:28 +0100 Subject: [PATCH 79/90] doobieengine docs fix --- .../za/co/absa/fadb/doobie/DoobieEngine.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala index 25ae0df3..9b1e1d03 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala @@ -27,13 +27,13 @@ import za.co.absa.fadb.exceptions.StatusException import scala.language.higherKinds /** - * [[DoobieEngine]] is a class that extends `DBEngine` with `F` as the effect type. - * It uses Doobie's `Transactor[F]` to execute SQL queries. + * [[DoobieEngine]] is a class that extends [[DBEngine]] with `F` as the effect type. + * It uses Doobie's [[Transactor]] to execute SQL queries. * - * `Async` is needed because Doobie requires it for non-blocking database operations. + * [[Async]] is needed because Doobie requires it for non-blocking database operations. * - * @param transactor the Doobie transactor for executing SQL queries - * @tparam F the effect type, which must have an `Async` instance + * @param transactor the Doobie [[Transactor]] for executing SQL queries + * @tparam F the effect type, which must have an [[Async]] instance */ class DoobieEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[F] { @@ -45,7 +45,7 @@ class DoobieEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[ * Executes a Doobie query and returns the result as an `F[Seq[R]]`. * * @param query the Doobie query to execute - * @param readR the `Read[R]` instance used to read the query result into `R` + * @param readR the [[Read]] instance used to read the query result into `R` * @return the query result as an `F[Seq[R]]` */ private def executeQuery[R](query: QueryType[R])(implicit readR: Read[R]): F[Seq[R]] = { @@ -56,7 +56,7 @@ class DoobieEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[ * Executes a Doobie query and returns the result as an `F[Either[StatusException, R]]`. * * @param query the Doobie query to execute - * @param readStatusWithDataR the `Read[StatusWithData[R]]` instance used to read the query result into `StatusWithData[R]` + * @param readStatusWithDataR the [[Read]] instance used to read the query result into `StatusWithData[R]` * @return the query result as an `F[Either[StatusException, R]]` */ private def executeQueryWithStatus[R]( From 8c7a17af4694515d9b55ff9bd2835098e61766a5 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 08:59:54 +0100 Subject: [PATCH 80/90] doobieengine docs fix --- .../src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala index 9b1e1d03..91251f69 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala @@ -28,11 +28,11 @@ import scala.language.higherKinds /** * [[DoobieEngine]] is a class that extends [[DBEngine]] with `F` as the effect type. - * It uses Doobie's [[Transactor]] to execute SQL queries. + * It uses Doobie's [[doobie.Transactor]] to execute SQL queries. * * [[Async]] is needed because Doobie requires it for non-blocking database operations. * - * @param transactor the Doobie [[Transactor]] for executing SQL queries + * @param transactor the Doobie [[doobie.Transactor]] for executing SQL queries * @tparam F the effect type, which must have an [[Async]] instance */ class DoobieEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[F] { From 57ec7378509149a8d8c31862acb6ad8c9eb87a00 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 09:02:09 +0100 Subject: [PATCH 81/90] doobieengine docs fix --- .../main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala index 91251f69..0acc5877 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala @@ -28,12 +28,12 @@ import scala.language.higherKinds /** * [[DoobieEngine]] is a class that extends [[DBEngine]] with `F` as the effect type. - * It uses Doobie's [[doobie.Transactor]] to execute SQL queries. + * It uses [[doobie.Transactor]] to execute SQL queries. * - * [[Async]] is needed because Doobie requires it for non-blocking database operations. + * [[cats.effect.Async]] is needed because Doobie requires it for non-blocking database operations. * - * @param transactor the Doobie [[doobie.Transactor]] for executing SQL queries - * @tparam F the effect type, which must have an [[Async]] instance + * @param transactor the [[doobie.Transactor]] for executing SQL queries + * @tparam F the effect type, which must have an [[cats.effect.Async]] instance */ class DoobieEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[F] { From d14b7d4481ef79367f7ce57cd6d61c13a6ea6930 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 09:06:49 +0100 Subject: [PATCH 82/90] doobiequery docs fix --- .../za/co/absa/fadb/doobie/DoobieQuery.scala | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala index 0909d363..f4560333 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala @@ -23,21 +23,21 @@ import za.co.absa.fadb.status.FunctionStatus import za.co.absa.fadb.{FunctionStatusWithData, Query, QueryWithStatus} /** - * `DoobieQuery` is a class that extends `Query` with `R` as the result type. - * It uses Doobie's `Fragment` to represent SQL queries. + * [[DoobieQuery]] is a class that extends [[za.co.absa.fadb.Query]] with `R` as the result type. + * It uses [[doobie.Fragment]] to represent SQL queries. * - * @param fragment the Doobie fragment representing the SQL query - * @param readR the `Read[R]` instance used to read the query result into `R` + * @param fragment the [[doobie.Fragment]] representing the SQL query + * @param readR the [[Read]] instance used to read the query result into `R` */ class DoobieQuery[R](val fragment: Fragment)(implicit val readR: Read[R]) extends Query[R] /** - * `DoobieQueryWithStatus` is a class that extends `QueryWithStatus` with `R` as the result type. - * It uses Doobie's `Fragment` to represent SQL queries. + * [[DoobieQueryWithStatus]] is a class that extends [[za.co.absa.fadb.QueryWithStatus]] with `R` as the result type. + * It uses [[doobie.Fragment]] to represent SQL queries. * - * @param fragment the Doobie fragment representing the SQL query + * @param fragment the [[doobie.Fragment]] representing the SQL query * @param checkStatus the function to check the status of the query - * @param readStatusWithDataR the `Read[StatusWithData[R]]` instance used to read the query result into `StatusWithData[R]` + * @param readStatusWithDataR the [[Read]] instance used to read the query result into `StatusWithData[R]` */ class DoobieQueryWithStatus[R]( val fragment: Fragment, @@ -45,7 +45,7 @@ class DoobieQueryWithStatus[R]( )(implicit val readStatusWithDataR: Read[StatusWithData[R]]) extends QueryWithStatus[StatusWithData[R], R, R] { - /* + /** * Processes the status of the query and returns the status with data * @param initialResult - the initial result of the query * @return the status with data @@ -53,7 +53,7 @@ class DoobieQueryWithStatus[R]( override def processStatus(initialResult: StatusWithData[R]): FunctionStatusWithData[R] = FunctionStatusWithData(FunctionStatus(initialResult.status, initialResult.statusText), initialResult.data) - /* + /** * Converts the status with data to either a status exception or the data * @param statusWithData - the status with data * @return either a status exception or the data From 228177c3a555a13b66c7aea03a9b04408f513543 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 09:26:12 +0100 Subject: [PATCH 83/90] doobiefunction, doobiequery docs fix --- .../za/co/absa/fadb/doobie/DoobieEngine.scala | 6 +- .../co/absa/fadb/doobie/DoobieFunction.scala | 59 ++++++++++--------- .../za/co/absa/fadb/doobie/DoobieQuery.scala | 4 +- .../fadb/doobie/DoobieStreamingEngine.scala | 10 ++-- 4 files changed, 40 insertions(+), 39 deletions(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala index 0acc5877..dfb8e3d2 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieEngine.scala @@ -27,7 +27,7 @@ import za.co.absa.fadb.exceptions.StatusException import scala.language.higherKinds /** - * [[DoobieEngine]] is a class that extends [[DBEngine]] with `F` as the effect type. + * [[DoobieEngine]] is a class that extends [[za.co.absa.fadb.DBEngine]] with `F` as the effect type. * It uses [[doobie.Transactor]] to execute SQL queries. * * [[cats.effect.Async]] is needed because Doobie requires it for non-blocking database operations. @@ -45,7 +45,7 @@ class DoobieEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[ * Executes a Doobie query and returns the result as an `F[Seq[R]]`. * * @param query the Doobie query to execute - * @param readR the [[Read]] instance used to read the query result into `R` + * @param readR the [[doobie.Read]] instance used to read the query result into `R` * @return the query result as an `F[Seq[R]]` */ private def executeQuery[R](query: QueryType[R])(implicit readR: Read[R]): F[Seq[R]] = { @@ -56,7 +56,7 @@ class DoobieEngine[F[_]: Async](val transactor: Transactor[F]) extends DBEngine[ * Executes a Doobie query and returns the result as an `F[Either[StatusException, R]]`. * * @param query the Doobie query to execute - * @param readStatusWithDataR the [[Read]] instance used to read the query result into `StatusWithData[R]` + * @param readStatusWithDataR the [[doobie.Read]] instance used to read the query result into `StatusWithData[R]` * @return the query result as an `F[Either[StatusException, R]]` */ private def executeQueryWithStatus[R]( diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index 41ce7516..32584bce 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -28,56 +28,56 @@ import scala.language.higherKinds trait DoobieFunctionBase[R] { /** - * The `Read[R]` instance used to read the query result into `R`. + * The [[doobie.Read]] instance used to read the query result into `R`. */ implicit val readR: Read[R] } /** - * `DoobieFunction` provides support for executing database functions using Doobie. + * [[DoobieFunction]] provides support for executing database functions using Doobie. * * @tparam I the input type of the function * @tparam R the result type of the function */ private[doobie] trait DoobieFunction[I, R] extends DoobieFunctionBase[R] { /** - * Generates a Doobie `Fragment` representing the SQL query for the function. + * Generates a [[doobie.Fragment]] representing the SQL query for the function. * * @param values the input values for the function - * @return the Doobie `Fragment` representing the SQL query + * @return the [[doobie.Fragment]] representing the SQL query */ def sql(values: I)(implicit read: Read[R]): Fragment /** - * Generates a `DoobieQuery[R]` representing the SQL query for the function. + * Generates a [[DoobieQuery]] for type `R` representing the SQL query for the function. * * @param values the input values for the function - * @return the `DoobieQuery[R]` representing the SQL query + * @return the [[DoobieQuery]] for type `R` representing the SQL query */ protected def query(values: I): DoobieQuery[R] = new DoobieQuery[R](sql(values)) } private[doobie] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBase[R] { /** - * The `Read[StatusWithData[R]]` instance used to read the query result with status into `StatusWithData[R]`. + * The [[doobie.Read]] instance used to read the query result with status into `StatusWithData[R]`. */ implicit def readStatusWithDataR(implicit readR: Read[R]): Read[StatusWithData[R]] = Read[(Int, String, R)].map { case (status, status_text, data) => StatusWithData(status, status_text, data) } /** - * Generates a Doobie `Fragment` representing the SQL query for the function. + * Generates a [[doobie.Fragment]] representing the SQL query for the function. * * @param values the input values for the function - * @return the Doobie `Fragment` representing the SQL query + * @return the [[doobie.Fragment]] representing the SQL query */ def sql(values: I)(implicit read: Read[StatusWithData[R]]): Fragment /** - * Generates a `DoobieQueryWithStatus[R]` representing the SQL query for the function. + * Generates a [[DoobieQueryWithStatus]] for type `R` representing the SQL query for the function. * * @param values the input values for the function - * @return the `DoobieQueryWithStatus[R]` representing the SQL query + * @return the [[DoobieQueryWithStatus]] for type `R` representing the SQL query */ protected def query(values: I): DoobieQueryWithStatus[R] = new DoobieQueryWithStatus[R](sql(values), checkStatus) @@ -86,21 +86,21 @@ private[doobie] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBase[ } /** - * `DoobieFunction` is an object that contains several abstract classes extending different types of database functions. - * These classes use Doobie's `Fragment` to represent SQL queries and `DoobieEngine` to execute them. + * [[DoobieFunction]] is an object that contains several abstract classes extending different types of database functions. + * These classes use [[doobie.Fragment]] to represent SQL queries and [[DoobieEngine]] to execute them. */ object DoobieFunction { /** - * `DoobieSingleResultFunctionWithStatus` is an abstract class that extends `DBSingleResultFunctionWithStatus` - * with `DoobieEngine` as the engine type. + * [[DoobieSingleResultFunctionWithStatus]] is an abstract class that extends + * [[za.co.absa.fadb.DBFunction.DBSingleResultFunctionWithStatus]] with [[DoobieEngine]] as the engine type. * It represents a database function that returns a single result with status. * * @param functionNameOverride the optional override for the function name * @param schema the database schema - * @param dbEngine the `DoobieEngine` instance used to execute SQL queries - * @param readR the `Read[R]` instance used to read the query result into `R` - * @param readSelectWithStatus the `Read[StatusWithData[R]]` instance used to read the query result with status into `StatusWithData[R]` - * @tparam F the effect type, which must have an `Async` and a `Monad` instance + * @param dbEngine the [[DoobieEngine]] instance used to execute SQL queries + * @param readR the [[doobie.Read]] instance used to read the query result into `R` + * @param readSelectWithStatus the [[doobie.Read]] instance used to read the query result into `StatusWithData[R]` + * @tparam F the effect type, which must have an [[cats.effect.Async]] instance */ abstract class DoobieSingleResultFunctionWithStatus[I, R, F[_]: Async]( functionNameOverride: Option[String] = None @@ -112,14 +112,15 @@ object DoobieFunction { with DoobieFunctionWithStatus[I, R] /** - * `DoobieSingleResultFunction` is an abstract class that extends `DBSingleResultFunction` with `DoobieEngine` as the engine type. + * [[DoobieSingleResultFunction]] is an abstract class that extends + * [[za.co.absa.fadb.DBFunction.DBSingleResultFunction]] with [[DoobieEngine]] as the engine type. * It represents a database function that returns a single result. * * @param functionNameOverride the optional override for the function name * @param schema the database schema - * @param dbEngine the `DoobieEngine` instance used to execute SQL queries - * @param readR the `Read[R]` instance used to read the query result into `R` - * @tparam F the effect type, which must have an `Async` and a `Monad` instance + * @param dbEngine the [[DoobieEngine]] instance used to execute SQL queries + * @param readR the [[doobie.Read]] instance used to read the query result into `R` + * @tparam F the effect type, which must have an [[cats.effect.Async]] instance */ abstract class DoobieSingleResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)( implicit override val schema: DBSchema, @@ -129,8 +130,8 @@ object DoobieFunction { with DoobieFunction[I, R] /** - * `DoobieMultipleResultFunction` is an abstract class that extends `DBMultipleResultFunction` - * with `DoobieEngine` as the engine type. + * [[DoobieMultipleResultFunction]] is an abstract class that extends + * [[za.co.absa.fadb.DBFunction.DBMultipleResultFunction]] with [[DoobieEngine]] as the engine type. * It represents a database function that returns multiple results. */ abstract class DoobieMultipleResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)( @@ -141,8 +142,8 @@ object DoobieFunction { with DoobieFunction[I, R] /** - * `DoobieStreamingResultFunction` is an abstract class that extends `DBStreamingFunction` - * with `DoobieStreamingEngine` as the engine type. + * [[DoobieStreamingResultFunction]] is an abstract class that extends + * [[za.co.absa.fadb.streaming.DBStreamingFunction]] with [[DoobieStreamingEngine]] as the engine type. * It represents a database function that returns a stream of results. */ abstract class DoobieStreamingResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)( @@ -153,8 +154,8 @@ object DoobieFunction { with DoobieFunction[I, R] /** - * `DoobieOptionalResultFunction` is an abstract class that extends `DBOptionalResultFunction` - * with `DoobieEngine` as the engine type. + * [[DoobieOptionalResultFunction]] is an abstract class that extends + * [[za.co.absa.fadb.DBFunction.DBOptionalResultFunction]] with [[DoobieEngine]] as the engine type. * It represents a database function that returns an optional result. */ abstract class DoobieOptionalResultFunction[I, R, F[_]: Async](functionNameOverride: Option[String] = None)( diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala index f4560333..f670ad6b 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieQuery.scala @@ -27,7 +27,7 @@ import za.co.absa.fadb.{FunctionStatusWithData, Query, QueryWithStatus} * It uses [[doobie.Fragment]] to represent SQL queries. * * @param fragment the [[doobie.Fragment]] representing the SQL query - * @param readR the [[Read]] instance used to read the query result into `R` + * @param readR the [[doobie.Read]] instance used to read the query result into `R` */ class DoobieQuery[R](val fragment: Fragment)(implicit val readR: Read[R]) extends Query[R] @@ -37,7 +37,7 @@ class DoobieQuery[R](val fragment: Fragment)(implicit val readR: Read[R]) extend * * @param fragment the [[doobie.Fragment]] representing the SQL query * @param checkStatus the function to check the status of the query - * @param readStatusWithDataR the [[Read]] instance used to read the query result into `StatusWithData[R]` + * @param readStatusWithDataR the [[doobie.Read]] instance used to read the query result into `StatusWithData[R]` */ class DoobieQueryWithStatus[R]( val fragment: Fragment, diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala index 4eca0785..52b5f2c0 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala @@ -25,7 +25,7 @@ import za.co.absa.fadb.streaming.DBStreamingEngine import scala.language.higherKinds /** - * `DoobieStreamingEngine` is a class that represents a database engine. + * [[DoobieStreamingEngine]] is a class that represents a database engine. * It provides methods to execute streaming queries from a database. * @tparam F - The type of the context in which the database queries are executed. */ @@ -38,8 +38,8 @@ class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], defaultC /** * Executes a Doobie query and returns the result as an `fs2.Stream[F, R]`. * - * @param query the Doobie query to execute - * @param readR the `Read[R]` instance used to read the query result into `R` + * @param query the [[DoobieQuery]] to execute + * @param readR the [[Read]] instance used to read the query result into `R` * @return the query result as an `fs2.Stream[F, R]` */ override def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] = @@ -50,7 +50,7 @@ class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], defaultC * * @param query the Doobie query to execute * @param chunkSize the chunk size to use when streaming the query result - * @param readR the `Read[R]` instance used to read the query result into `R` + * @param readR the [[Read]] instance used to read the query result into `R` * @return the query result as an `fs2.Stream[F, R]` */ override def runStreamingWithChunkSize[R](query: QueryType[R], chunkSize: Int): fs2.Stream[F, R] = @@ -61,7 +61,7 @@ class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], defaultC * * @param query the Doobie query to execute * @param chunkSize the chunk size to use when streaming the query result - * @param readR the `Read[R]` instance used to read the query result into `R` + * @param readR the [[Read]] instance used to read the query result into `R` * @return the query result as an `fs2.Stream[F, R]` */ private def executeStreamingQuery[R](query: QueryType[R], chunkSize: Int)(implicit readR: Read[R]): fs2.Stream[F, R] = { From 2d1fc37f149adc52155e06dce92d0c7a20acada6 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 09:31:32 +0100 Subject: [PATCH 84/90] doobiefunction, doobiequery docs fix --- .../main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala index 32584bce..eb338d4f 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieFunction.scala @@ -86,13 +86,13 @@ private[doobie] trait DoobieFunctionWithStatus[I, R] extends DoobieFunctionBase[ } /** - * [[DoobieFunction]] is an object that contains several abstract classes extending different types of database functions. + * An object that contains several abstract classes extending different types of database functions. * These classes use [[doobie.Fragment]] to represent SQL queries and [[DoobieEngine]] to execute them. */ object DoobieFunction { /** * [[DoobieSingleResultFunctionWithStatus]] is an abstract class that extends - * [[za.co.absa.fadb.DBFunction.DBSingleResultFunctionWithStatus]] with [[DoobieEngine]] as the engine type. + * [[za.co.absa.fadb.DBFunctionWithStatus]] with [[DoobieEngine]] as the engine type. * It represents a database function that returns a single result with status. * * @param functionNameOverride the optional override for the function name From db1d6e5732df5b02e9ba342b50f0868c535029fc Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 09:35:19 +0100 Subject: [PATCH 85/90] DoobieStreamingEngine docs fix --- .../za/co/absa/fadb/doobie/DoobieStreamingEngine.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala index 52b5f2c0..30a91ab9 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala @@ -39,7 +39,7 @@ class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], defaultC * Executes a Doobie query and returns the result as an `fs2.Stream[F, R]`. * * @param query the [[DoobieQuery]] to execute - * @param readR the [[Read]] instance used to read the query result into `R` + * @param readR the [[doobie.Read]] instance used to read the query result into `R` * @return the query result as an `fs2.Stream[F, R]` */ override def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] = @@ -50,7 +50,7 @@ class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], defaultC * * @param query the Doobie query to execute * @param chunkSize the chunk size to use when streaming the query result - * @param readR the [[Read]] instance used to read the query result into `R` + * @param readR the [[doobie.Read]] instance used to read the query result into `R` * @return the query result as an `fs2.Stream[F, R]` */ override def runStreamingWithChunkSize[R](query: QueryType[R], chunkSize: Int): fs2.Stream[F, R] = @@ -59,9 +59,9 @@ class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], defaultC /** * Executes a Doobie query and returns the result as an `fs2.Stream[F, R]`. * - * @param query the Doobie query to execute + * @param query the [[DoobieQuery]] to execute * @param chunkSize the chunk size to use when streaming the query result - * @param readR the [[Read]] instance used to read the query result into `R` + * @param readR the [[doobie.Read]] instance used to read the query result into `R` * @return the query result as an `fs2.Stream[F, R]` */ private def executeStreamingQuery[R](query: QueryType[R], chunkSize: Int)(implicit readR: Read[R]): fs2.Stream[F, R] = { From 7ce5288ddcba0b3b546484eb4eb4452aa23477cc Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 09:42:31 +0100 Subject: [PATCH 86/90] docs fix in slick module --- .../co/absa/fadb/slick/FaDbPostgresProfile.scala | 2 +- .../za/co/absa/fadb/slick/SlickFunction.scala | 14 +++++++------- .../za/co/absa/fadb/slick/SlickPgEngine.scala | 2 +- .../scala/za/co/absa/fadb/slick/SlickQuery.scala | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/FaDbPostgresProfile.scala b/slick/src/main/scala/za/co/absa/fadb/slick/FaDbPostgresProfile.scala index b0044d15..c90058f2 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/FaDbPostgresProfile.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/FaDbPostgresProfile.scala @@ -22,7 +22,7 @@ import za.co.absa.fadb.slick.support.PgUUIDSupport /** * DB profile recommended to use with SlickPgEngine to offer support for all extended Postgres types. * JSON is not included, as they are multiple JSON implementations. Choose the one of your liking and extend - * `FaDbPostgresProfile` with it. More on [SlickPG](https://github.com/tminglei/slick-pg/tree/master) page. + * [[FaDbPostgresProfile]] with it. More on [SlickPG](https://github.com/tminglei/slick-pg/tree/master) page. */ trait FaDbPostgresProfile extends ExPostgresProfile diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala index ade087c9..eabad68a 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickFunction.scala @@ -32,15 +32,15 @@ import scala.concurrent.Future private[slick] trait SlickFunctionBase[I, R] { /** - * The `GetResult[R]` instance used to read the query result into `R`. + * The [[slick.jdbc.GetResult]] instance used to read the query result into `R`. */ protected def slickConverter: GetResult[R] /** - * Generates a Slick `SQLActionBuilder` representing the SQL query for the function. + * Generates a Slick [[slick.jdbc.SQLActionBuilder]] representing the SQL query for the function. * * @param values the input values for the function - * @return the Slick `SQLActionBuilder` representing the SQL query + * @return the Slick [[slick.jdbc.SQLActionBuilder]] representing the SQL query */ protected def sql(values: I): SQLActionBuilder @@ -50,10 +50,10 @@ private[slick] trait SlickFunctionBase[I, R] { trait SlickFunction[I, R] extends SlickFunctionBase[I, R] { /** - * Generates a `SlickQuery[R]` representing the SQL query for the function. + * Generates a [[SlickQuery]] for type `R` representing the SQL query for the function. * * @param values the input values for the function - * @return the `SlickQuery[R]` representing the SQL query + * @return the [[SlickQuery]] for type `R` representing the SQL query */ protected def query(values: I): SlickQuery[R] = new SlickQuery(sql(values), slickConverter) } @@ -61,10 +61,10 @@ trait SlickFunction[I, R] extends SlickFunctionBase[I, R] { private[slick] trait SlickFunctionWithStatus[I, R] extends SlickFunctionBase[I, R] { /** - * Generates a `SlickQueryWithStatus[R]` representing the SQL query for the function with status support. + * Generates a [[SlickQueryWithStatus]] for type `R` representing the SQL query for the function with status support. * * @param values the input values for the function - * @return the `SlickQueryWithStatus[R]` representing the SQL query + * @return the [[SlickQueryWithStatus]] for type `R` representing the SQL query */ protected def query(values: I): SlickQueryWithStatus[R] = new SlickQueryWithStatus[R](sql(values), slickConverter, checkStatus) diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala index 61eba4c3..2b32a4f1 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgEngine.scala @@ -25,7 +25,7 @@ import scala.concurrent.{ExecutionContext, Future} import scala.language.higherKinds /** - * [[DBEngine]] based on the Slick library in the Postgres flavor + * [[za.co.absa.fadb.DBEngine]] based on the Slick library in the Postgres flavor * * @param db - the Slick database */ diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala index 13b9f5bb..7adf1da4 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickQuery.scala @@ -24,7 +24,7 @@ import za.co.absa.fadb.{FunctionStatusWithData, Query, QueryWithStatus} /** * SQL query representation for Slick * @param sql - the SQL query in Slick format - * @param getResult - function that converts the [[slick.jdbc.PositionedResult slick.PositionedResult]] + * @param getResult - function that converts the [[slick.jdbc.PositionedResult]] * (the result of Slick execution) into the desired `R` type * @tparam R - the return type of the query */ @@ -33,7 +33,7 @@ class SlickQuery[R](val sql: SQLActionBuilder, val getResult: GetResult[R]) exte /** * SQL query representation for Slick with status * @param sql - the SQL query in Slick format - * @param getResult - function that converts the [[slick.jdbc.PositionedResult slick.PositionedResult]] + * @param getResult - function that converts the [[slick.jdbc.PositionedResult]] * (the result of Slick execution) into the desired `R` type * @tparam R - the return type of the query */ From c70ff3b5fcd41c31ba78e094cdff4136871dd493 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 09:47:21 +0100 Subject: [PATCH 87/90] docs fix fs2 reference --- .../co/absa/fadb/doobie/DoobieStreamingEngine.scala | 12 ++++++------ .../slick/streaming/SlickPgStreamingEngine.scala | 10 +++++----- .../co/absa/fadb/streaming/DBStreamingEngine.scala | 10 +++++----- .../co/absa/fadb/streaming/DBStreamingFunction.scala | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala index 30a91ab9..f66c2f23 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala @@ -36,33 +36,33 @@ class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], defaultC type QueryType[R] = DoobieQuery[R] /** - * Executes a Doobie query and returns the result as an `fs2.Stream[F, R]`. + * Executes a Doobie query and returns the result as an [[fs2.Stream]] for effect type `F` and value type `R`. * * @param query the [[DoobieQuery]] to execute * @param readR the [[doobie.Read]] instance used to read the query result into `R` - * @return the query result as an `fs2.Stream[F, R]` + * @return the query result as an [[fs2.Stream]] for effect type `F` and value type `R` */ override def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] = executeStreamingQuery(query, defaultChunkSize)(query.readR) /** - * Executes a Doobie query and returns the result as an `fs2.Stream[F, R]`. + * Executes a Doobie query and returns the result as an [[fs2.Stream]] for effect type `F` and value type `R`. * * @param query the Doobie query to execute * @param chunkSize the chunk size to use when streaming the query result * @param readR the [[doobie.Read]] instance used to read the query result into `R` - * @return the query result as an `fs2.Stream[F, R]` + * @return the query result as an [[fs2.Stream]] for effect type `F` and value type `R` */ override def runStreamingWithChunkSize[R](query: QueryType[R], chunkSize: Int): fs2.Stream[F, R] = executeStreamingQuery(query, chunkSize)(query.readR) /** - * Executes a Doobie query and returns the result as an `fs2.Stream[F, R]`. + * Executes a Doobie query and returns the result as an [[fs2.Stream]] for effect type `F` and value type `R`. * * @param query the [[DoobieQuery]] to execute * @param chunkSize the chunk size to use when streaming the query result * @param readR the [[doobie.Read]] instance used to read the query result into `R` - * @return the query result as an `fs2.Stream[F, R]` + * @return the query result as an [[fs2.Stream]] for effect type `F` and value type `R` */ private def executeStreamingQuery[R](query: QueryType[R], chunkSize: Int)(implicit readR: Read[R]): fs2.Stream[F, R] = { query.fragment.query[R].streamWithChunkSize(chunkSize).transact(transactor) diff --git a/slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickPgStreamingEngine.scala b/slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickPgStreamingEngine.scala index 1ba223ef..3b38a3b3 100644 --- a/slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickPgStreamingEngine.scala +++ b/slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickPgStreamingEngine.scala @@ -25,7 +25,7 @@ import za.co.absa.fadb.streaming.DBStreamingEngine import scala.language.higherKinds /** - * `SlickStreamingEngine` is a class that represents a database engine. + * [[SlickStreamingEngine]] is a class that represents a database engine. * It provides methods to execute streaming queries from a database. * @tparam F - The type of the context in which the database queries are executed. */ @@ -35,10 +35,10 @@ class SlickPgStreamingEngine[F[_]: Async](val db: Database, defaultChunkSize: In type QueryType[R] = SlickQuery[R] /** - * Executes a Slick query and returns the result as an `fs2.Stream[F, R]`. + * Executes a Slick query and returns the result as an [[fs2.Stream]] for effect type `F` and value type `R`. * * @param query the Slick query to execute - * @return the query result as an `fs2.Stream[F, R]` + * @return the query result as an [[fs2.Stream]] for effect type `F` and value type `R` */ def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] = { val slickPublisher = db.stream(query.sql.as[R](query.getResult)) @@ -46,11 +46,11 @@ class SlickPgStreamingEngine[F[_]: Async](val db: Database, defaultChunkSize: In } /** - * Executes a Slick query and returns the result as an `fs2.Stream[F, R]`. + * Executes a Slick query and returns the result as an [[fs2.Stream]] for effect type `F` and value type `R`. * * @param query the Slick query to execute * @param chunkSize the chunk size to use when streaming the query result - * @return the query result as an `fs2.Stream[F, R]` + * @return the query result as an [[fs2.Stream]] for effect type `F` and value type `R` */ def runStreamingWithChunkSize[R](query: QueryType[R], chunkSize: Int): fs2.Stream[F, R] = { val slickPublisher = db.stream(query.sql.as[R](query.getResult)) diff --git a/streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingEngine.scala b/streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingEngine.scala index 2d1b1aaf..c1bf9e4f 100644 --- a/streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingEngine.scala +++ b/streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingEngine.scala @@ -21,7 +21,7 @@ import za.co.absa.fadb.Query import scala.language.higherKinds /** - * `DBStreamingEngine` is an abstract class that represents a database engine. + * [[DBStreamingEngine]] is an abstract class that represents a database engine. * It provides methods to execute streaming queries from a database. * @tparam F - The type of the context in which the database queries are executed. */ @@ -34,19 +34,19 @@ abstract class DBStreamingEngine[F[_]] { type QueryType[R] <: Query[R] /** - * Executes a query and returns the result as an `fs2.Stream[F, R]`. + * Executes a query and returns the result as an [[fs2.Stream]] for effect type `F` and value type `R`. * * @param query the query to execute - * @return the query result as an `fs2.Stream[F, R]` + * @return the query result as an [[fs2.Stream]] for effect type `F` and value type `R` */ def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] /** - * Executes a query and returns the result as an `fs2.Stream[F, R]`. + * Executes a query and returns the result as an [[fs2.Stream]] for effect type `F` and value type `R`. * * @param query the query to execute * @param chunkSize the chunk size to use when streaming the query result - * @return the query result as an `fs2.Stream[F, R]` + * @return the query result as an [[fs2.Stream]] for effect type `F` and value type `R` */ def runStreamingWithChunkSize[R](query: QueryType[R], chunkSize: Int): fs2.Stream[F, R] diff --git a/streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingFunction.scala b/streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingFunction.scala index 6d164c3b..e0445215 100644 --- a/streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingFunction.scala +++ b/streaming/src/main/scala/za/co/absa/fadb/streaming/DBStreamingFunction.scala @@ -21,7 +21,7 @@ import za.co.absa.fadb.{DBEngine, DBFunctionFabric, DBSchema} import scala.language.higherKinds /** - * `DBStreamingFunction` is an abstract class that represents a database function returning a stream of results. + * [[DBStreamingFunction]] is an abstract class that represents a database function returning a stream of results. * * @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name. * @param schema - The schema the function belongs to. From c000455bf783d073668648f11593116cdf9d9058 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 10:04:57 +0100 Subject: [PATCH 88/90] readme update --- README.md | 32 ++++++++++++++++++++++++++++---- build.sbt | 8 ++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e55187d5..92542a6f 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ ___ - [Usage](#usage) - [Concepts](#concepts) - [Slick module](#slick-module) +- [Slick streaming module](#slick-streaming-module) +- [Doobie module](#doobie-module) - [Testing](#testing) - [How to Release](#how-to-release) @@ -54,10 +56,12 @@ Currently, the library is developed with Postgres as the target DB. But the appr #### Sbt -Import one of the two available module at the moment. Slick module works with Scala Futures. Doobie module works with any effect type (typically IO or ZIO) provided cats effect's Async instance is available. +Slick module works with Scala Futures. Slick-streaming provides also streaming support for Slick with fs2 streams and any effect type provided cats effect's Async instance is available. +Doobie module provides both non-streaming and streaming functions and works with any effect type (typically IO or ZIO) provided cats effect's Async instance is available. ```scala libraryDependencies *= "za.co.absa.fa-db" %% "slick" % "X.Y.Z" +libraryDependencies *= "za.co.absa.fa-db" %% "slick-streaming" % "X.Y.Z" libraryDependencies *= "za.co.absa.fa-db" %% "doobie" % "X.Y.Z" ``` @@ -67,7 +71,9 @@ libraryDependencies *= "za.co.absa.fa-db" %% "doobie" % "X.Y.Z" Modules: * Core [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/core_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/core_2.12) +* Streaming [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/streaming_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/streaming_2.12) * Slick [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick_2.12) +* Slick-streaming [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick-streaming_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick-streaming_2.12) * Doobie [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/doobie_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/doobie_2.12) ```xml @@ -76,6 +82,11 @@ Modules: slick_2.12 ${latest_version} + + za.co.absa.fa-db + slick-streaming_2.12 + ${latest_version} + za.co.absa.fa-db doobie_2.12 @@ -86,7 +97,9 @@ Modules: ### Scala 2.13 Modules: * Core [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/core_2.13/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/core_2.13) +* Streaming [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/streaming_2.13/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/streaming_2.13) * Slick [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick_2.13/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick_2.13) +* Slick-streaming [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick-streaming_2.13/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/slick-streaming_2.13) * Doobie [![Maven Central](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/doobie_2.13/badge.svg)](https://maven-badges.herokuapp.com/maven-central/za.co.absa.fa-db/doobie_2.13) ```xml @@ -95,6 +108,11 @@ Modules: slick_2.13 ${latest_version} + + za.co.absa.fa-db + slick-streaming_2.13 + ${latest_version} + za.co.absa.fa-db doobie_2.13 @@ -116,12 +134,10 @@ As the name suggests it runs on [Slick library](https://github.com/slick/slick) It brings: * `class SlickPgEngine` - implementation of _Core_'s `DBEngine` executing the queries via Slick -* `class SlickPgStreamingEngine` - implementation of _Core_'s `DBStreamingEngine` executing the queries via Slick in a streaming fashion * `class SlickSingleResultFunction` - abstract class for DB functions returning single result * `class SlickMultipleResultFunction` - abstract class for DB functions returning sequence of results * `class SlickOptionalResultFunction` - abstract class for DB functions returning optional result * `class SlickSingleResultFunctionWithStatus` - abstract class for DB functions with status handling; it requires an implementation of `StatusHandling` to be mixed-in (`StandardStatusHandling` available out-of-the-box) -* `class SlickStreamingResultFunction` - abstract class for DB functions returning sequence of results (fs2.Stream) in a streaming fashion * `trait FaDbPostgresProfile` - to bring support for Postgres and its extended data types in one class (except JSON, as there are multiple implementations for this data type in _Slick-Pg_) * `object FaDbPostgresProfile` - instance of the above trait for direct use @@ -143,6 +159,13 @@ val hStore: Option[Map[String, String]] = pr.nextHStoreOption val macAddr: Option[MacAddrString] = pr.nextMacAddrOption ``` +## Slick streaming module + +It additionally brings: + +* `class SlickPgStreamingEngine` - implementation of _Streaming_'s `DBStreamingEngine` executing the queries via Slick in a streaming fashion +* `class SlickStreamingResultFunction` - abstract class for DB functions returning sequence of results (fs2.Stream) in a streaming fashion + ## Doobie module As the name suggests it runs on [Doobie library](https://tpolecat.github.io/doobie/). The main benefit of the module is that it allows to use any effect type (typically IO or ZIO) therefore is more suitable for functional programming. It also brings in the [Doobie-Postgres library](https://tpolecat.github.io/doobie/docs/14-PostgreSQL.html) for extended Postgres type support. @@ -150,12 +173,13 @@ As the name suggests it runs on [Doobie library](https://tpolecat.github.io/doob It brings: * `class DoobieEngine` - implementation of _Core_'s `DBEngine` executing the queries via Doobie. The class is type parameterized with the effect type. -* `class DoobieStreamingEngine` - implementation of _Core_'s `DBStreamingEngine` executing the queries via Doobie in a streaming fashion. The class is type parameterized with the effect type. +* `class DoobieStreamingEngine` - implementation of _Streamings_'s `DBStreamingEngine` executing the queries via Doobie in a streaming fashion. The class is type parameterized with the effect type. * `class DoobieSingleResultFunction` - abstract class for DB functions returning single result * `class DoobieMultipleResultFunction` - abstract class for DB functions returning sequence of results * `class DoobieOptionalResultFunction` - abstract class for DB functions returning optional result * `class DoobieSingleResultFunctionWithStatus` - abstract class for DB functions with status handling; it requires an implementation of `StatusHandling` to be mixed-in (`StandardStatusHandling` available out-of-the-box) * `class DoobieStreamingResultFunction` - abstract class for DB functions returning sequence of results (fs2.Stream) in a streaming fashion +* Since Doobie also interoperates with ZIO, there is an example of how a database connection can be properly established within a ZIO application. Please see [this file](doobie/zio-setup.md) for more details. ## Testing diff --git a/build.sbt b/build.sbt index c7d8ca59..55237126 100644 --- a/build.sbt +++ b/build.sbt @@ -80,7 +80,7 @@ lazy val faDBStreaming = (project in file("streaming")) libraryDependencies ++= streamingDependencies(scalaVersion.value), javacOptions ++= commonJavacOptions, scalacOptions ++= commonScalacOptions, - (Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, // printScalaVersion is run with compile + (Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, Defaults.itSettings, ).dependsOn(faDbCore) .settings( @@ -95,7 +95,7 @@ lazy val faDBSlick = (project in file("slick")) libraryDependencies ++= slickDependencies(scalaVersion.value), javacOptions ++= commonJavacOptions, scalacOptions ++= commonScalacOptions, - (Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, // printScalaVersion is run with compile + (Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, Defaults.itSettings, ).dependsOn(faDbCore) .settings( @@ -110,7 +110,7 @@ lazy val faDBSlickStreaming = (project in file("slick-streaming")) libraryDependencies ++= slickStreamingDependencies(scalaVersion.value), javacOptions ++= commonJavacOptions, scalacOptions ++= commonScalacOptions, - (Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, // printScalaVersion is run with compile + (Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, Defaults.itSettings, ).dependsOn(faDbCore, faDBStreaming, faDBSlick) .settings( @@ -138,7 +138,7 @@ lazy val faDBExamples = (project in file("examples")) name := "examples", libraryDependencies ++= examplesDependencies(scalaVersion.value), Test / parallelExecution := false, - (Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, // printScalaVersion is run with compile + (Compile / compile) := ((Compile / compile) dependsOn printScalaVersion).value, publish / skip := true ).dependsOn(faDbCore, faDBSlick) .settings( From 2fd7487b3d0bbe5854b1ac590da25b1a4c1833f9 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 10:34:53 +0100 Subject: [PATCH 89/90] alignment --- .../za/co/absa/fadb/doobie/DoobieStreamingEngine.scala | 2 -- project/Dependencies.scala | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala index f66c2f23..18addffc 100644 --- a/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala +++ b/doobie/src/main/scala/za/co/absa/fadb/doobie/DoobieStreamingEngine.scala @@ -39,7 +39,6 @@ class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], defaultC * Executes a Doobie query and returns the result as an [[fs2.Stream]] for effect type `F` and value type `R`. * * @param query the [[DoobieQuery]] to execute - * @param readR the [[doobie.Read]] instance used to read the query result into `R` * @return the query result as an [[fs2.Stream]] for effect type `F` and value type `R` */ override def runStreaming[R](query: QueryType[R]): fs2.Stream[F, R] = @@ -50,7 +49,6 @@ class DoobieStreamingEngine[F[_]: Async](val transactor: Transactor[F], defaultC * * @param query the Doobie query to execute * @param chunkSize the chunk size to use when streaming the query result - * @param readR the [[doobie.Read]] instance used to read the query result into `R` * @return the query result as an [[fs2.Stream]] for effect type `F` and value type `R` */ override def runStreamingWithChunkSize[R](query: QueryType[R], chunkSize: Int): fs2.Stream[F, R] = diff --git a/project/Dependencies.scala b/project/Dependencies.scala index bbd7d8d0..a2008c2e 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -19,7 +19,7 @@ import sbt._ object Dependencies { private def commonDependencies(scalaVersion: String): Seq[ModuleID] = Seq( - "org.typelevel" %% "cats-core" % "2.9.0", + "org.typelevel" %% "cats-core" % "2.9.0", "org.scalatest" %% "scalatest" % "3.1.0" % "test,it", "org.scalatest" %% "scalatest-flatspec" % "3.2.0" % "test,it", "org.scalatestplus" %% "mockito-1-10" % "3.1.0.0" % "test,it" @@ -56,8 +56,8 @@ object Dependencies { def doobieDependencies(scalaVersion: String): Seq[ModuleID] = { commonDependencies(scalaVersion) ++ Seq( - "org.tpolecat" %% "doobie-core" % "1.0.0-RC2", - "org.tpolecat" %% "doobie-hikari" % "1.0.0-RC2", + "org.tpolecat" %% "doobie-core" % "1.0.0-RC2", + "org.tpolecat" %% "doobie-hikari" % "1.0.0-RC2", "org.tpolecat" %% "doobie-postgres" % "1.0.0-RC2" ) } From a70daeef7199480ccd69d1ff6d556b4f754e1e07 Mon Sep 17 00:00:00 2001 From: Pavel Salamon Date: Thu, 4 Jan 2024 10:40:26 +0100 Subject: [PATCH 90/90] buid.sbt update --- build.sbt | 2 +- .../co/absa/fadb/slick/streaming/SlickPgStreamingEngine.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 55237126..88cb3763 100644 --- a/build.sbt +++ b/build.sbt @@ -49,7 +49,7 @@ lazy val commonJacocoExcludes: Seq[String] = Seq( ) lazy val parent = (project in file(".")) - .aggregate(faDbCore, faDBSlick, faDBDoobie, faDBExamples) + .aggregate(faDbCore, faDBStreaming, faDBSlick, faDBSlickStreaming, faDBDoobie, faDBExamples) .settings( name := "root", libraryDependencies ++= rootDependencies(scalaVersion.value), diff --git a/slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickPgStreamingEngine.scala b/slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickPgStreamingEngine.scala index 3b38a3b3..472d3f8a 100644 --- a/slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickPgStreamingEngine.scala +++ b/slick-streaming/src/main/scala/za/co/absa/fadb/slick/streaming/SlickPgStreamingEngine.scala @@ -25,7 +25,7 @@ import za.co.absa.fadb.streaming.DBStreamingEngine import scala.language.higherKinds /** - * [[SlickStreamingEngine]] is a class that represents a database engine. + * [[SlickPgStreamingEngine]] is a class that represents a database engine. * It provides methods to execute streaming queries from a database. * @tparam F - The type of the context in which the database queries are executed. */