From 09e2c9cb1d0eeb4ebb589fdbddaa99f72cd22cb3 Mon Sep 17 00:00:00 2001 From: David Benedeki Date: Mon, 29 May 2023 10:56:24 +0200 Subject: [PATCH 1/7] #5: Traits to support status_code reaction * New exceptions for status code related failures * Documentation describing the standard status codes, how they have been agreed by the team * Mix-in trait for `DBFunction` for handling the status codes * `trait DBFunctionFabric` created to easy the creation of `DBFunction` mixing traits * `SlickPgFunction` refactored somewhat for easier `StatusHandling` trait integration --- .../scala/za/co/absa/fadb/DBFunction.scala | 9 ++- .../za/co/absa/fadb/DBFunctionFabric.scala | 27 +++++++ .../fadb/exceptions/DBFailException.scala | 10 ++- .../main/scala/za/co/absa/fadb/package.scala | 6 ++ .../fadb/statushandling/StatusException.scala | 41 ++++++++++ .../fadb/statushandling/StatusHandling.scala | 42 ++++++++++ .../statushandling/fadbstandard/README.md | 79 +++++++++++++++++++ .../fadbstandard/StandardStatusHandling.scala | 40 ++++++++++ .../examples/enceladus/DatasetSchema.scala | 55 ++++++------- .../enceladus/DatasetSchemaSuite.scala | 13 +-- .../co/absa/fadb/slick/SlickPgFunction.scala | 17 +++- 11 files changed, 301 insertions(+), 38 deletions(-) create mode 100644 core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala create mode 100644 core/src/main/scala/za/co/absa/fadb/statushandling/StatusException.scala create mode 100644 core/src/main/scala/za/co/absa/fadb/statushandling/StatusHandling.scala create mode 100644 core/src/main/scala/za/co/absa/fadb/statushandling/fadbstandard/README.md create mode 100644 core/src/main/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandling.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 b72f758e..d6432de3 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunction.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunction.scala @@ -29,7 +29,7 @@ import scala.concurrent.Future * @tparam T - the type covering the input fields of the database function * @tparam R - the type covering the returned fields from the database function */ -abstract class DBFunction[E, T, R](schema: DBSchema[E], functionNameOverride: Option[String] = None) { +abstract class DBFunction[E, T, R](schema: DBSchema[E], functionNameOverride: Option[String] = None) extends DBFunctionFabric { val functionName: String = { val fn = functionNameOverride.getOrElse(schema.objectNameFromClassName(getClass)) if (schema.schemaName.isEmpty) { @@ -39,6 +39,13 @@ abstract class DBFunction[E, T, R](schema: DBSchema[E], functionNameOverride: Op } } + /** + * For the given output it returns a function to execute the SQL query and interpret the results. + * Basically it should create a function which contains a query to be executable and executed on on the [[DBExecutor]] + * and transforming the result of that query to result type. + * @param values - the input values of the DB function (stored procedure) + * @return - the query function that when provided an executor will return the result of the DB function call + */ protected def queryFunction(values: T): QueryFunction[E, R] } diff --git a/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala b/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala new file mode 100644 index 00000000..1a9c7dcc --- /dev/null +++ b/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2023 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 + +/** + * 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 { + def functionName: String + + protected def fieldsToSelect: Seq[String] = Seq.empty +} 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 index 7afa943b..c390cf0d 100644 --- a/core/src/main/scala/za/co/absa/fadb/exceptions/DBFailException.scala +++ b/core/src/main/scala/za/co/absa/fadb/exceptions/DBFailException.scala @@ -16,4 +16,12 @@ package za.co.absa.fadb.exceptions -case class DBFailException(status: Int, message: String) extends Exception(message) +/** + * General Fa-DB exception class + * @param message - the message describing the reason of exception + */ +class DBFailException(message: String) extends Exception(message) + +object DBFailException { + def apply(message: String): DBFailException = new DBFailException(message) +} diff --git a/core/src/main/scala/za/co/absa/fadb/package.scala b/core/src/main/scala/za/co/absa/fadb/package.scala index 3d353940..14f51ef0 100644 --- a/core/src/main/scala/za/co/absa/fadb/package.scala +++ b/core/src/main/scala/za/co/absa/fadb/package.scala @@ -19,5 +19,11 @@ package za.co.absa import scala.concurrent.Future package object fadb { + /** + * Represents a database query call (in the model of Fa-Db a call to a DB stored procedure). When provided a DB + * connection (of type [[DBExecutor]]) it executes the query and transforms it to the desired result type sequence. + * @tparam E - the type of the DB connection to execute on + * @tparam R - the type of result + */ type QueryFunction[E, R] = E => Future[Seq[R]] } diff --git a/core/src/main/scala/za/co/absa/fadb/statushandling/StatusException.scala b/core/src/main/scala/za/co/absa/fadb/statushandling/StatusException.scala new file mode 100644 index 00000000..aa068500 --- /dev/null +++ b/core/src/main/scala/za/co/absa/fadb/statushandling/StatusException.scala @@ -0,0 +1,41 @@ +/* + * 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.statushandling + +import za.co.absa.fadb.exceptions.DBFailException + +/** + * Exception caused by status signaling a failure in DB function execution + * @param status - the status that caused the error + * @param statusText - the status text explaining the status code + */ +class StatusException(val status: Int, statusText: String) extends DBFailException(statusText) { + def statusText: String = getMessage +} + +object StatusException { + class ServerMisconfigurationException(status: Int, statusText: String) extends StatusException(status, statusText) + + class DataConflictException(status: Int, statusText: String) extends StatusException(status, statusText) + + class DataNotFoundException(status: Int, statusText: String) extends StatusException(status, statusText) + + class ErrorInDataException(status: Int, statusText: String) extends StatusException(status, statusText) + + class OtherStatusException(status: Int, statusText: String) extends StatusException(status, statusText) + +} diff --git a/core/src/main/scala/za/co/absa/fadb/statushandling/StatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/statushandling/StatusHandling.scala new file mode 100644 index 00000000..06bb4720 --- /dev/null +++ b/core/src/main/scala/za/co/absa/fadb/statushandling/StatusHandling.scala @@ -0,0 +1,42 @@ +/* + * 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.statushandling + +import za.co.absa.fadb.DBFunctionFabric +import za.co.absa.fadb.statushandling.StatusHandling.{defaultStatusFieldName, defaultStatusTextFieldName} + +import scala.util.Try + +/** + * A basis for mix-in traits for [[DBFunction]] that support `status` and `status_text` for easier handling + */ +trait StatusHandling extends DBFunctionFabric{ + + def statusFieldName: String = defaultStatusFieldName + def statusTextFieldName: String = defaultStatusTextFieldName + + override protected def fieldsToSelect: Seq[String] = { + Seq(statusFieldName, statusTextFieldName) ++ super.fieldsToSelect + } + + protected def checkStatus(status: Integer, statusTex: String): Try[Unit] +} + +object StatusHandling { + val defaultStatusFieldName = "status" + val defaultStatusTextFieldName = "status_test" +} diff --git a/core/src/main/scala/za/co/absa/fadb/statushandling/fadbstandard/README.md b/core/src/main/scala/za/co/absa/fadb/statushandling/fadbstandard/README.md new file mode 100644 index 00000000..74e6d6c0 --- /dev/null +++ b/core/src/main/scala/za/co/absa/fadb/statushandling/fadbstandard/README.md @@ -0,0 +1,79 @@ +# Intro + +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 +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. + +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) +* The possible returned status codes and their meaning should be described in the function comments + +# Codes + +The codes to be double digit + +## OK/Correct outcome + +### 10-19 + +| 10 | general ok | +| 11 | data created | +| 12 | data updated | +| 14 | no op needed | +| 15 | data deleted | + +Rest unspecified OK status, to be agreed in contract between DB and app + +## Server misconfiguration + +### 20-29 + +Specific to the database application, should be shared over all functions in that DB. + +## Data conflict + +### 30-39 + +| 30 | general data conflict | +| 31 | referenced data does not allow execution of the request | + +Rest of the codes meaning depends on agreement in contact between DB and app + +## Data not found + +### 40-49 + +| 40 | requested data 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 + +## Error in data + +### 50-89 + +| 50-59 | generally incorrect data (when obvious from context) | +| 60-69 | missing value (usually NULL) | +| 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 + +### 90-99 + +Rest of the codes meaning depends on agreement in contact between DB and app diff --git a/core/src/main/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandling.scala new file mode 100644 index 00000000..147be3fc --- /dev/null +++ b/core/src/main/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandling.scala @@ -0,0 +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.statushandling.fadbstandard + +import za.co.absa.fadb.exceptions.DBFailException +import za.co.absa.fadb.statushandling.StatusHandling +import za.co.absa.fadb.statushandling.StatusException._ + +import scala.util.{Failure, Success, Try} + +/** + * A mix in trait for [[DBFunction]] for standard handling of `status` and `status_text` fields. + */ +trait StandardStatusHandling extends StatusHandling { + override protected def checkStatus(status: Integer, statusText: String): Try[Unit] = { + status / 10 match { + case 1 => Success(Unit) + case 2 => Failure(new ServerMisconfigurationException(status, statusText)) + case 3 => Failure(new DataConflictException(status, statusText)) + case 4 => Failure(new DataNotFoundException(status, statusText)) + case 5 | 6 | 7 | 8 => Failure(new ErrorInDataException(status, statusText)) + case 9 => Failure(new OtherStatusException(status, statusText)) + case _ => Failure(DBFailException(s"Status out of range - with status: $status and status text: '$statusText'")) + } + } +} 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 b307ec50..90e3fb75 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,17 +16,17 @@ package za.co.absa.fadb.examples.enceladus -import za.co.absa.fadb.{DBSchema, QueryFunction} +import za.co.absa.fadb.{DBSchema} import za.co.absa.fadb.slick.{SlickPgExecutor, SlickPgFunction} import za.co.absa.fadb.naming_conventions.SnakeCaseNaming.Implicits.namingConvention -import slick.jdbc.GetResult +import slick.jdbc.{GetResult, SQLActionBuilder} import za.co.absa.fadb.DBFunction._ import slick.jdbc.PostgresProfile.api._ -import za.co.absa.fadb.exceptions.DBFailException import java.sql.Timestamp import scala.concurrent.Future import DatasetSchema._ +import za.co.absa.fadb.statushandling.StatusException class DatasetSchema(executor: SlickPgExecutor) extends DBSchema(executor) { @@ -67,64 +67,65 @@ object DatasetSchema { val status: Int = r.<< val statusText: String = r.<< if (status != 200) { - throw DBFailException(status, statusText) + throw new StatusException(status, statusText) } Schema(r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<) }) final class AddSchema(implicit schema: DBSchema[ExecutorEngineType]) extends DBUniqueFunction[ExecutorEngineType, SchemaInput, Long](schema) - with SlickPgFunction { + with SlickPgFunction[SchemaInput, Long] { - override protected def queryFunction(values: SchemaInput): QueryFunction[ExecutorEngineType, Long] = { - val gr:GetResult[Long] = GetResult(r => { - val status: Int = r.<< - val statusText: String = r.<< - if (status != 201) throw DBFailException(status, statusText) - r.<< - }) - val sql = - sql"""SELECT A.status, A.status_text, A.id_schema_version + override protected def sqlToCallFunction(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;""" + } - makeQueryFunction(sql)(gr) + override protected def resultConverter: GetResult[Long] = { + val gr:GetResult[Long] = GetResult(r => { + val status: Int = r.<< + val statusText: String = r.<< + if (status != 201) throw new StatusException(status, statusText) + r.<< + }) + gr } } final class GetSchema(implicit schema: DBSchema[ExecutorEngineType]) extends DBUniqueFunction[ExecutorEngineType, (String, Option[Int]), Schema](schema) - with SlickPgFunction { + with SlickPgFunction[(String, Option[Int]), Schema] { def apply(id: Long): Future[Schema] = { val sql = sql"""SELECT A.* FROM #$functionName($id) A;""" - schema.unique(makeQueryFunction[Schema](sql)) + + schema.unique(makeQueryFunction(sql)(resultConverter)) } - override protected def queryFunction(values: (String, Option[Int])): QueryFunction[ExecutorEngineType, Schema] = { - val sql = - sql"""SELECT A.* + override protected def sqlToCallFunction(values: (String, Option[Int])): SQLActionBuilder = { + sql"""SELECT A.* FROM #$functionName(${values._1}, ${values._2}) A;""" - - makeQueryFunction[Schema](sql) } + + override protected def resultConverter: GetResult[Schema] = DatasetSchema.GetSchemaImplicit } final class ListSchemas(implicit schema: DBSchema[ExecutorEngineType]) extends DBSeqFunction[ExecutorEngineType, Boolean, SchemaHeader](schema) - with SlickPgFunction { + with SlickPgFunction[Boolean, SchemaHeader] { override def apply(values: Boolean = false): Future[Seq[SchemaHeader]] = super.apply(values) - override protected def queryFunction(values: Boolean): QueryFunction[ExecutorEngineType, SchemaHeader] = { - val sql = - sql"""SELECT A.schema_name, A.schema_latest_version + override protected def sqlToCallFunction(values: Boolean): SQLActionBuilder = { + sql"""SELECT A.schema_name, A.schema_latest_version FROM #$functionName($values) as A;""" - makeQueryFunction[SchemaHeader](sql) } + + override protected def resultConverter: GetResult[SchemaHeader] = DatasetSchema.SchemaHeaderImplicit } } 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 473df309..905fc232 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 @@ -22,6 +22,7 @@ import za.co.absa.fadb.slick.SlickPgExecutor import za.co.absa.fadb.examples.enceladus.DatasetSchema._ import za.co.absa.fadb.exceptions.DBFailException import slick.jdbc.PostgresProfile.api._ +import za.co.absa.fadb.statushandling.StatusException import scala.concurrent.Await import scala.concurrent.duration.Duration @@ -31,8 +32,8 @@ class DatasetSchemaSuite extends AnyWordSpec with Matchers { private val executor = new SlickPgExecutor(db) private val schemas = new DatasetSchema(executor) - private def checkException(exception: DBFailException): Unit = { - println(s"Requested failed with: ${exception.status} - ${exception.message}") + private def checkException(exception: StatusException): Unit = { + println(s"Requested failed with: ${exception.status} - ${exception.statusText}") } // test cases are set to be ignored now, as they are not idempotent and require other project's (Enceladus) data structures @@ -67,14 +68,14 @@ class DatasetSchemaSuite extends AnyWordSpec with Matchers { } "fail" when { "schema does not exist" ignore { - val exception = intercept[DBFailException] { + val exception = intercept[StatusException] { val gs = schemas.getSchema(("xxx", None)) Await.result(gs, Duration.Inf) } checkException(exception) } "requested schema version does not exist" ignore { - val exception = intercept[DBFailException] { + val exception = intercept[StatusException] { val gs = schemas.getSchema(("aaa", Some(1000))) Await.result(gs, Duration.Inf) } @@ -104,7 +105,7 @@ class DatasetSchemaSuite extends AnyWordSpec with Matchers { fields = Option("""{"foo": "bar"}"""), userName = "david" ) - val exception = intercept[DBFailException] { + val exception = intercept[StatusException] { Await.result(schemas.addSchema(schemaInput), Duration.Inf) } checkException(exception) @@ -117,7 +118,7 @@ class DatasetSchemaSuite extends AnyWordSpec with Matchers { fields = Option("""{"not_getting_in": "1"}"""), userName = "david" ) - val exception = intercept[DBFailException] { + val exception = intercept[StatusException] { Await.result(schemas.addSchema(schemaInput), Duration.Inf) } checkException(exception) diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgFunction.scala index 068812e5..a2f3ba5f 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgFunction.scala @@ -18,12 +18,23 @@ package za.co.absa.fadb.slick import slick.jdbc.{GetResult, SQLActionBuilder} import slick.jdbc.PostgresProfile.api._ -import za.co.absa.fadb.QueryFunction +import za.co.absa.fadb.{DBFunctionFabric, QueryFunction} -trait SlickPgFunction { - def makeQueryFunction[R](sql: SQLActionBuilder)(implicit rconv: GetResult[R]): QueryFunction[Database, R] = { +trait SlickPgFunction[T, R] extends DBFunctionFabric { + + protected def sqlToCallFunction(values: T): SQLActionBuilder = ??? //TODO make abstract + + protected def resultConverter: GetResult[R] = ??? //TODO make abstract + + protected def makeQueryFunction(sql: SQLActionBuilder)(implicit rconv: GetResult[R]): QueryFunction[Database, R] = { val query = sql.as[R] val resultFnc = {db: Database => db.run(query)} resultFnc } + + protected def queryFunction(values: T): QueryFunction[Database, R] = { + val converter = resultConverter + val functionSql = sqlToCallFunction(values) + makeQueryFunction(functionSql)(converter) + } } From aa11e056e8ad8c424de34600a085f141805e6bc3 Mon Sep 17 00:00:00 2001 From: David Benedeki Date: Mon, 29 May 2023 16:20:57 +0200 Subject: [PATCH 2/7] * fixed License * Added UT --- .../za/co/absa/fadb/DBFunctionFabric.scala | 2 +- .../StandardStatusHandlingTest.scala | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 core/src/test/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandlingTest.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 1a9c7dcc..c2fb5276 100644 --- a/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala +++ b/core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala @@ -1,5 +1,5 @@ /* - * Copyright 2023 ABSA Group Limited + * 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. diff --git a/core/src/test/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandlingTest.scala b/core/src/test/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandlingTest.scala new file mode 100644 index 00000000..753a8902 --- /dev/null +++ b/core/src/test/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandlingTest.scala @@ -0,0 +1,54 @@ +/* + * Copyright 2023 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.statushandling.fadbstandard + +import org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.statushandling.StatusException +import za.co.absa.fadb.statushandling.StatusException._ + +import scala.reflect.ClassTag +import scala.util.{Failure, Try} + +class StandardStatusHandlingTest extends AnyFunSuite { + test("Verify checkStatus error mapping") { + class StandardStatusHandlingForTest extends StandardStatusHandling { + override def checkStatus(status: Integer, statusText: String): Try[Unit] = super.checkStatus(status, statusText) + override def functionName: String = "Never needed" + } + + 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 == status) + assert(failure.statusText == statusText) + } + implicit val standardStatusHandling: StandardStatusHandlingForTest = new StandardStatusHandlingForTest + + assert(standardStatusHandling.checkStatus(10, "OK").isSuccess) + 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") + assert(standardStatusHandling.checkStatus(101, "Server is wrongly set up").isFailure) + } +} From 300d8c2e4f284f4b752264de5402cddba7bdc98e Mon Sep 17 00:00:00 2001 From: David Benedeki Date: Mon, 29 May 2023 18:24:17 +0200 Subject: [PATCH 3/7] * One more license fix --- .../fadbstandard/StandardStatusHandlingTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandlingTest.scala b/core/src/test/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandlingTest.scala index 753a8902..a371b0b9 100644 --- a/core/src/test/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandlingTest.scala +++ b/core/src/test/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandlingTest.scala @@ -1,5 +1,5 @@ /* - * Copyright 2023 ABSA Group Limited + * 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. From bfa5d53a7d313879012574793f640d672973fa60 Mon Sep 17 00:00:00 2001 From: David Benedeki Date: Mon, 29 May 2023 19:03:06 +0200 Subject: [PATCH 4/7] * Made some functions abstract as supposed --- .../main/scala/za/co/absa/fadb/slick/SlickPgFunction.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgFunction.scala b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgFunction.scala index a2f3ba5f..c6fbaa0f 100644 --- a/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgFunction.scala +++ b/slick/src/main/scala/za/co/absa/fadb/slick/SlickPgFunction.scala @@ -22,9 +22,9 @@ import za.co.absa.fadb.{DBFunctionFabric, QueryFunction} trait SlickPgFunction[T, R] extends DBFunctionFabric { - protected def sqlToCallFunction(values: T): SQLActionBuilder = ??? //TODO make abstract + protected def sqlToCallFunction(values: T): SQLActionBuilder - protected def resultConverter: GetResult[R] = ??? //TODO make abstract + protected def resultConverter: GetResult[R] protected def makeQueryFunction(sql: SQLActionBuilder)(implicit rconv: GetResult[R]): QueryFunction[Database, R] = { val query = sql.as[R] From a5d9e1b0b5cecc60b54cadcc4707ea471474eca7 Mon Sep 17 00:00:00 2001 From: David Benedeki <14905969+benedeki@users.noreply.github.com> Date: Tue, 30 May 2023 09:29:44 +0200 Subject: [PATCH 5/7] Update core/src/test/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandlingTest.scala Co-authored-by: miroslavpojer --- .../fadbstandard/StandardStatusHandlingTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandlingTest.scala b/core/src/test/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandlingTest.scala index a371b0b9..ccc4a94f 100644 --- a/core/src/test/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandlingTest.scala +++ b/core/src/test/scala/za/co/absa/fadb/statushandling/fadbstandard/StandardStatusHandlingTest.scala @@ -21,7 +21,7 @@ import za.co.absa.fadb.statushandling.StatusException import za.co.absa.fadb.statushandling.StatusException._ import scala.reflect.ClassTag -import scala.util.{Failure, Try} +import scala.util.Try class StandardStatusHandlingTest extends AnyFunSuite { test("Verify checkStatus error mapping") { From 8eff72b624029b7b2210dfb7d71b83dfcdbbb4e7 Mon Sep 17 00:00:00 2001 From: David Benedeki <14905969+benedeki@users.noreply.github.com> Date: Tue, 30 May 2023 09:30:24 +0200 Subject: [PATCH 6/7] Update examples/src/main/scala/za/co/absa/fadb/examples/enceladus/DatasetSchema.scala Co-authored-by: miroslavpojer --- .../za/co/absa/fadb/examples/enceladus/DatasetSchema.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 90e3fb75..3ecb9703 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,7 +16,7 @@ package za.co.absa.fadb.examples.enceladus -import za.co.absa.fadb.{DBSchema} +import za.co.absa.fadb.DBSchema import za.co.absa.fadb.slick.{SlickPgExecutor, SlickPgFunction} import za.co.absa.fadb.naming_conventions.SnakeCaseNaming.Implicits.namingConvention import slick.jdbc.{GetResult, SQLActionBuilder} From 32dd2f02b6abb66917b08bb19f0782eba8b62802 Mon Sep 17 00:00:00 2001 From: David Benedeki <14905969+benedeki@users.noreply.github.com> Date: Tue, 30 May 2023 16:46:24 +0200 Subject: [PATCH 7/7] Apply suggestions from code review Co-authored-by: miroslavpojer --- .../scala/za/co/absa/fadb/statushandling/StatusHandling.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/za/co/absa/fadb/statushandling/StatusHandling.scala b/core/src/main/scala/za/co/absa/fadb/statushandling/StatusHandling.scala index 06bb4720..30e0c865 100644 --- a/core/src/main/scala/za/co/absa/fadb/statushandling/StatusHandling.scala +++ b/core/src/main/scala/za/co/absa/fadb/statushandling/StatusHandling.scala @@ -38,5 +38,5 @@ trait StatusHandling extends DBFunctionFabric{ object StatusHandling { val defaultStatusFieldName = "status" - val defaultStatusTextFieldName = "status_test" + val defaultStatusTextFieldName = "status_text" }