Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/schema function independent #102

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 64 additions & 106 deletions core/src/main/scala/za/co/absa/fadb/DBFunction.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,34 +30,24 @@ 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 {

/* alternative constructors for different availability of input parameters */
def this(functionNameOverride: String)
(implicit schema: DBSchema, dBEngine: E) = {
this(Option(functionNameOverride))(schema, dBEngine)
abstract class DBFunction[I, R, E <: DBEngine](functionNameOverride: Option[String] = None, schema: Option[DBSchema])
(implicit val dBEngine: E, val namingConvention: NamingConvention)
extends DBFunctionFabric {

// Alternative constructors for different availability of input parameters
def this(functionNameOverride: String, schema: DBSchema)
(implicit dBEngine: E, namingConvention: NamingConvention) = {
this(Option(functionNameOverride), Option(schema))
}

def this(schema: DBSchema, functionNameOverride: String)
(implicit dBEngine: E) = {
this(Option(functionNameOverride))(schema, dBEngine)
def this(functionNameOverride: String)
(implicit schema: Option[DBSchema], dBEngine: E, namingConvention: NamingConvention) = {
this(Option(functionNameOverride), schema)
}

/* 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)
(implicit dBEngine: E, namingConvention: NamingConvention) = {
this(None, Option(schema))
}

/**
Expand All @@ -71,17 +61,15 @@ 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"
def functionName: String = {
schema match {
case Some(s) if s.schemaName.nonEmpty =>
s"${s.schemaName}.${functionNameOverride.getOrElse(namingConvention.fromClassNamePerConvention(getClass))}"
case _ =>
functionNameOverride.getOrElse(namingConvention.fromClassNamePerConvention(getClass))
}
}

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
Expand All @@ -107,34 +95,24 @@ 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)
}
abstract class DBMultipleResultFunction[I, R, E <: DBEngine](functionNameOverride: Option[String] = None, schema: Option[DBSchema] = None)
(implicit override val dBEngine: E, override val namingConvention: NamingConvention)
extends DBFunction[I, R, E](functionNameOverride, schema) {

def this(schema: DBSchema)
(implicit dBEngine: E) = {
this(None)(schema, dBEngine)
}
def this(functionNameOverride: String, schema: DBSchema)
(implicit dBEngine: E, namingConvention: NamingConvention) = {
this(Option(functionNameOverride), Option(schema))
}

def this(dBEngine: E, functionNameOverride: String)
(implicit schema: DBSchema) = {
this(Option(functionNameOverride))(schema, dBEngine)
}
def this(functionNameOverride: String)
(implicit schema: Option[DBSchema], dBEngine: E, namingConvention: NamingConvention) = {
this(Option(functionNameOverride), schema)
}

def this(dBEngine: E)
(implicit schema: DBSchema) = {
this(None)(schema, dBEngine)
}
def this(schema: DBSchema)
(implicit dBEngine: E, namingConvention: NamingConvention) = {
this(None, Option(schema))
}

/**
* For easy and convenient execution of the DB function call
Expand All @@ -156,34 +134,24 @@ 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)
}
abstract class DBSingleResultFunction[I, R, E <: DBEngine](functionNameOverride: Option[String] = None, schema: Option[DBSchema] = None)
(implicit override val dBEngine: E, override val namingConvention: NamingConvention)
extends DBFunction[I, R, E](functionNameOverride, schema) {

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(functionNameOverride: String, schema: DBSchema)
(implicit dBEngine: E, namingConvention: NamingConvention) = {
this(Option(functionNameOverride), Option(schema))
}

def this(dBEngine: E, functionNameOverride: String)
(implicit schema: DBSchema) = {
this(Option(functionNameOverride))(schema, dBEngine)
}
def this(functionNameOverride: String)
(implicit schema: Option[DBSchema], dBEngine: E, namingConvention: NamingConvention) = {
this(Option(functionNameOverride), schema)
}

def this(dBEngine: E)
(implicit schema: DBSchema) = {
this(None)(schema, dBEngine)
}
def this(schema: DBSchema)
(implicit dBEngine: E, namingConvention: NamingConvention) = {
this(None, Option(schema))
}

/**
* For easy and convenient execution of the DB function call
Expand All @@ -204,34 +172,24 @@ 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)
}
abstract class DBOptionalResultFunction[I, R, E <: DBEngine](functionNameOverride: Option[String] = None, schema: Option[DBSchema] = None)
(implicit override val dBEngine: E, override val namingConvention: NamingConvention)
extends DBFunction[I, R, E](functionNameOverride, schema) {

def this(schema: DBSchema)
(implicit dBEngine: E) = {
this(None)(schema, dBEngine)
}
def this(functionNameOverride: String, schema: DBSchema)
(implicit dBEngine: E, namingConvention: NamingConvention) = {
this(Option(functionNameOverride), Option(schema))
}

def this(dBEngine: E, functionNameOverride: String)
(implicit schema: DBSchema) = {
this(Option(functionNameOverride))(schema, dBEngine)
}
def this(functionNameOverride: String)
(implicit schema: Option[DBSchema], dBEngine: E, namingConvention: NamingConvention) = {
this(Option(functionNameOverride), schema)
}

def this(dBEngine: E)
(implicit schema: DBSchema) = {
this(None)(schema, dBEngine)
}
def this(schema: DBSchema)
(implicit dBEngine: E, namingConvention: NamingConvention) = {
this(None, Option(schema))
}

/**
* For easy and convenient execution of the DB function call
Expand Down
58 changes: 16 additions & 42 deletions core/src/main/scala/za/co/absa/fadb/DBSchema.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,60 +19,34 @@ 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)
// Alternative constructors for different availability of input parameters
def this(schemaNameOverride: String)(implicit namingConvention: NamingConvention) = {
this(Option(schemaNameOverride))
}

def this(dBEngine: DBEngine, schemaNameOverride: String)
(implicit namingConvention: NamingConvention) {
this(Option(schemaNameOverride))(dBEngine, namingConvention)
def this()(implicit namingConvention: NamingConvention) = {
this(None)
}

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)
}

/**
* 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
*/
val schemaName: String = schemaNameOverride.getOrElse(objectNameFromClassName(getClass))
val schemaName: String = schemaNameOverride.getOrElse(objectNameFromClassName(this.getClass))

}
22 changes: 16 additions & 6 deletions core/src/test/scala/za/co/absa/fadb/DBFunctionSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ 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) {
override protected def query(values: Unit): dBEngine.QueryType[Unit] = neverHappens
case class MyFunction(schema: DBSchema) extends DBFunction(schema) {
override protected def query(values: Nothing): dBEngine.QueryType[Nothing] = neverHappens
}

val fnc1 = MyFunction(FooNamed)
Expand All @@ -49,8 +49,8 @@ class DBFunctionSuite extends AnyFunSuite {
}

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
case class MyFunction(schema: DBSchema) extends DBFunction("bar", schema) {
override protected def query(values: Nothing): dBEngine.QueryType[Nothing] = neverHappens
}

val fnc1 = MyFunction(FooNamed)
Expand All @@ -60,4 +60,14 @@ class DBFunctionSuite extends AnyFunSuite {
assert(fnc2.functionName == "bar")
}

test("Function name check with no schema"){
case class MyFunction() extends DBFunction(None, None) {
override protected def query(values: Nothing): dBEngine.QueryType[Nothing] = neverHappens
}

val fnc = MyFunction()

assert(fnc.functionName == "my_function")
}

}
35 changes: 23 additions & 12 deletions core/src/test/scala/za/co/absa/fadb/DBSchemaSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,43 @@
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

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")
}

test("schema name with naming convention without override") {
object LowerCaseNamingConvention extends NamingConvention {
def stringPerConvention(original: String): String = original.toLowerCase
}
class Bar extends DBSchema()(LowerCaseNamingConvention)

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("bar")(LowerCaseNamingConvention)

val schema = new Bar
assert(schema.schemaName == "bar")
}

}
Loading
Loading