From 05f4a2130c4f402de531b975f22474699a112df1 Mon Sep 17 00:00:00 2001 From: Ladislav Sulak Date: Mon, 25 Mar 2024 11:33:18 +0100 Subject: [PATCH] #118: adding support for MultipleResultFunction with status for Doobie --- README.md | 1 + ...MultipleResultFunctionWithStatusTest.scala | 74 +++++++++++++++++++ .../za/co/absa/fadb/doobie/DoobieTest.scala | 1 + .../co/absa/fadb/doobie/DoobieFunction.scala | 13 ++++ 4 files changed, 89 insertions(+) create mode 100644 doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionWithStatusTest.scala diff --git a/README.md b/README.md index 6cb799cf..8930282a 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,7 @@ It brings: * `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 DoobieMultipleResultFunctionWithStatus` - as `DoobieSingleResultFunctionWithStatus` but for multiple record retrieval 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. diff --git a/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionWithStatusTest.scala b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionWithStatusTest.scala new file mode 100644 index 00000000..52176cab --- /dev/null +++ b/doobie/src/it/scala/za/co/absa/fadb/doobie/DoobieMultipleResultFunctionWithStatusTest.scala @@ -0,0 +1,74 @@ +/* + * 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 org.scalatest.funsuite.AnyFunSuite +import za.co.absa.fadb.DBSchema +import za.co.absa.fadb.doobie.DoobieFunction.DoobieMultipleResultFunctionWithStatus +import za.co.absa.fadb.status.handling.implementations.StandardStatusHandling + +class DoobieMultipleResultFunctionWithStatusTest extends AnyFunSuite with DoobieTest { + + private val getActorsByLastnameQueryFragments: GetActorsByLastnameQueryParameters => Seq[Fragment] = { + values => Seq(fr"${values.lastName}", fr"${values.firstName}") + } + + class GetActorsByLastname(implicit schema: DBSchema, dbEngine: DoobieEngine[IO]) + extends DoobieMultipleResultFunctionWithStatus[GetActorsByLastnameQueryParameters, Option[Actor], IO](getActorsByLastnameQueryFragments) + with StandardStatusHandling { + override def fieldsToSelect: Seq[String] = super.fieldsToSelect ++ Seq("actor_id", "first_name", "last_name") + } + + private val getActorsByLastname = new GetActorsByLastname()(Integration, new DoobieEngine(transactor)) + + test("Retrieving actor from database, full match") { + val expectedResultElem = Actor(50, "Liza", "Simpson") + val results = getActorsByLastname(GetActorsByLastnameQueryParameters("Simpson", Some("Liza"))).unsafeRunSync() + + results match { + case Left(_) => fail("should not be left") + case Right(value) => + assert(value.contains(expectedResultElem)) + } + } + + test("Retrieving actor from database, lastname match") { + val expectedResultElem = Actor(50, "Liza", "Simpson") + val results = getActorsByLastname(GetActorsByLastnameQueryParameters("Simpson")).unsafeRunSync() + + results match { + case Left(_) => fail("should not be left") + case Right(value) => + assert(value.contains(expectedResultElem)) + } + } + + test("Retrieving actor from database, no match") { + val results = getActorsByLastname(GetActorsByLastnameQueryParameters("TotallyNonExisting!")).unsafeRunSync() + + results match { + case Left(value) => + assert(value.status.statusText == "No actor found") + assert(value.status.statusCode == 41) + case Right(_) => fail("should not be right") + } + } +} 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 fe13f1aa..170c41bd 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 @@ -24,6 +24,7 @@ import za.co.absa.fadb.DBSchema trait DoobieTest { case class Actor(actorId: Int, firstName: String, lastName: String) case class GetActorsQueryParameters(firstName: Option[String], lastName: Option[String]) + case class GetActorsByLastnameQueryParameters(lastName: String, firstName: Option[String] = None) case class CreateActorRequestBody(firstName: String, lastName: String) import za.co.absa.fadb.naming.implementations.SnakeCaseNaming.Implicits._ 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 1070f2c8..9f8fec28 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 @@ -260,6 +260,19 @@ object DoobieFunction { ) extends DBMultipleResultFunction[I, R, DoobieEngine[F], F](functionNameOverride) with DoobieFunction[I, R, F] + /** + * `DoobieMultipleResultFunctionWithStatus` represents a db function that returns multiple results with statuses. + */ + abstract class DoobieMultipleResultFunctionWithStatus[I, R, F[_]]( + override val toFragmentsSeq: I => Seq[Fragment], + functionNameOverride: Option[String] = None + )(implicit + override val schema: DBSchema, + val dbEngine: DoobieEngine[F], + val readR: Read[R] + ) extends DBFunctionWithStatus[I, R, DoobieEngine[F], F](functionNameOverride) + with DoobieFunctionWithStatus[I, R, F] + /** * `DoobieOptionalResultFunction` represents a db function that returns an optional result. */