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..f3dcc2de 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -30,34 +30,12 @@ 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) - (implicit val schema: DBSchema, val dBEngine: E) extends DBFunctionFabric { +abstract class DBFunction[I, R, E <: DBEngine](schemaName: String, functionNameOverride: Option[String] = None) + (implicit val dBEngine: E, namingConvention: NamingConvention) 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) + // constructor that takes function name override as string instead of option + def this(schemaName: String, functionNameOverride: String)(implicit dBEngine: E, namingConvention: NamingConvention) { + this(schemaName, Option(functionNameOverride)) } /** @@ -72,16 +50,18 @@ abstract class DBFunction[I, R, E <: DBEngine](functionNameOverride: Option[Stri * 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" - } + val baseFunctionName = functionNameOverride.getOrElse { + val className = this.getClass.getSimpleName + val cleanClassName = className.lastIndexOf('$') match { + case -1 => className + case x => className.substring(0, x) + } + namingConvention.stringPerConvention(cleanClassName) + } + if (schemaName.isEmpty) baseFunctionName + else s"$schemaName.$baseFunctionName" } - 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 @@ -107,34 +87,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) - (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) - } + abstract class DBMultipleResultFunction[I, R, E <: DBEngine](schemaName: String, functionNameOverride: Option[String] = None) + (implicit dBEngine: E, namingConvention: NamingConvention) + extends DBFunction[I, R, E](schemaName, functionNameOverride) { /** * For easy and convenient execution of the DB function call @@ -156,34 +111,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 DBSingleResultFunction[I, R, E <: DBEngine](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) - } + abstract class DBSingleResultFunction[I, R, E <: DBEngine](schemaName: String, functionNameOverride: Option[String] = None) + (implicit dBEngine: E, namingConvention: NamingConvention) + extends DBFunction[I, R, E](schemaName, functionNameOverride) { /** * For easy and convenient execution of the DB function call @@ -204,34 +134,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 DBOptionalResultFunction[I, R, E <: DBEngine](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) - } + abstract class DBOptionalResultFunction[I, R, E <: DBEngine](schemaName: String, functionNameOverride: Option[String] = None) + (implicit dBEngine: E, namingConvention: NamingConvention) + extends DBFunction[I, R, E](schemaName, functionNameOverride) { /** * For easy and convenient execution of the DB function call 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..a53a5979 100644 --- a/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala +++ b/core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala @@ -33,28 +33,25 @@ class DBFunctionSuite extends AnyFunSuite { 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) { + case class MyFunction(schemaName: String) extends DBFunction[Unit, Unit, DBEngine](schemaName) { override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens } - val fnc1 = MyFunction(FooNamed) - val fnc2 = MyFunction(FooNameless) + val fnc1 = MyFunction("foo_named") + val fnc2 = MyFunction("") 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") { + case class MyFunction(schemaName: String) extends DBFunction[Unit, Unit, DBEngine](schemaName, "bar") { override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens } - val fnc1 = MyFunction(FooNamed) - val fnc2 = MyFunction(FooNameless) + val fnc1 = MyFunction("foo_named") + val fnc2 = MyFunction("") assert(fnc1.functionName == "foo_named.bar") assert(fnc2.functionName == "bar") diff --git a/core/src/test/scala/za/co/absa/fadb/DBSchemaSuite.scala b/core/src/test/scala/za/co/absa/fadb/DBSchemaSuite.scala deleted file mode 100644 index 8e7a90e4..00000000 --- a/core/src/test/scala/za/co/absa/fadb/DBSchemaSuite.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 - -import org.scalatest.funsuite.AnyFunSuite -import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits.namingConvention - -class DBSchemaSuite extends AnyFunSuite { - - test("schema name default") { - class Foo extends DBSchema - - val schema = new Foo - assert(schema.schemaName == "foo") - } - - test("schema name overridden") { - class Foo extends DBSchema("bar") - - val schema = new Foo - assert(schema.schemaName == "bar") - } - -} 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 deleted file mode 100644 index 09b39048..00000000 --- a/examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala +++ /dev/null @@ -1,122 +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.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/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 deleted file mode 100644 index f305d748..00000000 --- a/examples/src/test/scala/za/co/absa/fadb/examples/enceladus/DatasetSchemaSuite.scala +++ /dev/null @@ -1,128 +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.examples.enceladus - -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.slick.SlickPgEngine -import za.co.absa.fadb.status.StatusException - -import scala.concurrent.Await -import scala.concurrent.duration.Duration -import scala.concurrent.ExecutionContext.Implicits.global - -class DatasetSchemaSuite extends AnyWordSpec with Matchers { - private val db = Database.forConfig("menasdb") - private implicit val dbEngine: SlickPgEngine = new SlickPgEngine(db) - private val schemas = new DatasetSchema - - private def checkException(exception: StatusException): Unit = { - println(s"Requested failed with: ${exception.status.statusCode} - ${exception.status.statusText}") - } - - // test cases are set to be ignored now, as they are not idempotent and require other project's (Enceladus) data structures - - "listSchemas" ignore { - "list the schemas" should { - val ls = schemas.list() - val result = Await.result(ls, Duration.Inf) - result.foreach(println) - } - } - - "getSchema" ignore { - "return the particular schema" when { - "given name and version" should { - val ls = schemas.getSchema(("aaa", Option(1))) - val result = Await.result(ls, Duration.Inf) - println(result) - } - "given id" should { - val gs = schemas.getSchema(1000000000000051L) - val result = Await.result(gs, Duration.Inf) - println(result) - } - } - "return the latest schema version" when { - "only the schema name is given" should { - val ls = schemas.getSchema(("aaa", None)) - val result = Await.result(ls, Duration.Inf) - println(result) - } - } - "fail" when { - "schema does not exist" should { - val exception = intercept[StatusException] { - val gs = schemas.getSchema(("xxx", None)) - Await.result(gs, Duration.Inf) - } - checkException(exception) - } - "requested schema version does not exist" should { - val exception = intercept[StatusException] { - val gs = schemas.getSchema(("aaa", Some(1000))) - Await.result(gs, Duration.Inf) - } - checkException(exception) - } - } - } - - "addSchema" ignore { - "add a schema" should { - val schemaInput = SchemaInput( - schemaName = "bbe", - schemaVersion = 1, - schemaDescription = Option("Hello World"), - fields = Option("""{"lorem": "ipsum"}"""), - userName = "david" - ) - val result = Await.result(schemas.addSchema(schemaInput), Duration.Inf) - println(result) - } - "fail" when { - "Schema already exists" should { - val schemaInput = SchemaInput( - schemaName = "aaa", - schemaVersion = 2, - schemaDescription = Option("Updates"), - fields = Option("""{"foo": "bar"}"""), - userName = "david" - ) - val exception = intercept[StatusException] { - Await.result(schemas.addSchema(schemaInput), Duration.Inf) - } - checkException(exception) - } - "Schema version wrong" should { - val schemaInput = SchemaInput( - schemaName = "aaa", - schemaVersion = 1000, - schemaDescription = Option("Will fail"), - fields = Option("""{"not_getting_in": "1"}"""), - userName = "david" - ) - val exception = intercept[StatusException] { - Await.result(schemas.addSchema(schemaInput), Duration.Inf) - } - checkException(exception) - } - } - } -}