~jan0sch/smederee

Showing details for patch f15299aad27aeaa47f23fc0371b191779a40fa6a.
2023-03-31 (Fri), 6:45 PM - Jens Grassel - f15299aad27aeaa47f23fc0371b191779a40fa6a

Tickets: Add implementation for `ProjectRepository`.

- add `DoobieProjectRepository`
- add integration tests
Summary of changes
2 files added
  • modules/tickets/src/it/scala/de/smederee/tickets/DoobieProjectRepositoryTest.scala
  • modules/tickets/src/main/scala/de/smederee/tickets/DoobieProjectRepository.scala
diff -rN -u old-smederee/modules/tickets/src/it/scala/de/smederee/tickets/DoobieProjectRepositoryTest.scala new-smederee/modules/tickets/src/it/scala/de/smederee/tickets/DoobieProjectRepositoryTest.scala
--- old-smederee/modules/tickets/src/it/scala/de/smederee/tickets/DoobieProjectRepositoryTest.scala	1970-01-01 00:00:00.000000000 +0000
+++ new-smederee/modules/tickets/src/it/scala/de/smederee/tickets/DoobieProjectRepositoryTest.scala	2025-01-31 04:54:13.899274092 +0000
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022  Contributors as noted in the AUTHORS.md file
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.smederee.tickets
+
+import cats.effect._
+import cats.syntax.all._
+import de.smederee.tickets.Generators._
+import doobie._
+
+final class DoobieProjectRepositoryTest extends BaseSpec {
+  test("findProject must return the matching project") {
+    (genValidProjectOwner.sample, genValidProjects.sample) match {
+      case (Some(owner), Some(generatedProject :: projects)) =>
+        val project     = generatedProject.copy(owner = owner)
+        val dbConfig    = configuration.database
+        val tx          = Transactor.fromDriverManager[IO](dbConfig.driver, dbConfig.url, dbConfig.user, dbConfig.pass)
+        val projectRepo = new DoobieProjectRepository[IO](tx)
+        val test = for {
+          _ <- createTicketsUser(owner)
+          _ <- createTicketsProject(project)
+          _ <- projects.filterNot(_.name === project.name).traverse(p => createTicketsProject(p.copy(owner = owner)))
+          foundProject <- projectRepo.findProject(owner, project.name)
+        } yield foundProject
+        test.map { foundProject =>
+          assertEquals(foundProject, Some(project))
+        }
+      case _ => fail("Could not generate data samples!")
+    }
+  }
+
+  test("findProjectId must return the matching id") {
+    (genValidProjectOwner.sample, genValidProjects.sample) match {
+      case (Some(owner), Some(generatedProject :: projects)) =>
+        val project     = generatedProject.copy(owner = owner)
+        val dbConfig    = configuration.database
+        val tx          = Transactor.fromDriverManager[IO](dbConfig.driver, dbConfig.url, dbConfig.user, dbConfig.pass)
+        val projectRepo = new DoobieProjectRepository[IO](tx)
+        val test = for {
+          _ <- createTicketsUser(owner)
+          _ <- createTicketsProject(project)
+          _ <- projects.filterNot(_.name === project.name).traverse(p => createTicketsProject(p.copy(owner = owner)))
+          foundProjectId <- projectRepo.findProjectId(owner, project.name)
+          projectId      <- loadProjectId(owner.uid, project.name)
+        } yield (foundProjectId, projectId)
+        test.map { result =>
+          val (foundProjectId, projectId) = result
+          assertEquals(foundProjectId, projectId)
+        }
+      case _ => fail("Could not generate data samples!")
+    }
+  }
+
+  test("findProjectOwner must return the matching project owner") {
+    genValidProjectOwners.sample match {
+      case Some(owner :: owners) =>
+        val dbConfig    = configuration.database
+        val tx          = Transactor.fromDriverManager[IO](dbConfig.driver, dbConfig.url, dbConfig.user, dbConfig.pass)
+        val projectRepo = new DoobieProjectRepository[IO](tx)
+        val test = for {
+          _          <- createTicketsUser(owner)
+          _          <- owners.filterNot(_.name === owner.name).traverse(createTicketsUser)
+          foundOwner <- projectRepo.findProjectOwner(owner.name)
+        } yield foundOwner
+        test.map { foundOwner =>
+          assert(foundOwner.exists(_ === owner))
+        }
+      case _ => fail("Could not generate data samples!")
+    }
+  }
+}
diff -rN -u old-smederee/modules/tickets/src/main/scala/de/smederee/tickets/DoobieProjectRepository.scala new-smederee/modules/tickets/src/main/scala/de/smederee/tickets/DoobieProjectRepository.scala
--- old-smederee/modules/tickets/src/main/scala/de/smederee/tickets/DoobieProjectRepository.scala	1970-01-01 00:00:00.000000000 +0000
+++ new-smederee/modules/tickets/src/main/scala/de/smederee/tickets/DoobieProjectRepository.scala	2025-01-31 04:54:13.899274092 +0000
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022  Contributors as noted in the AUTHORS.md file
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.smederee.tickets
+
+import java.util.UUID
+
+import cats.effect._
+import de.smederee.email.EmailAddress
+import doobie._
+import doobie.implicits._
+import doobie.postgres.implicits._
+
+final class DoobieProjectRepository[F[_]: Sync](tx: Transactor[F]) extends ProjectRepository[F] {
+
+  given Meta[EmailAddress]       = Meta[String].timap(EmailAddress.apply)(_.toString)
+  given Meta[ProjectDescription] = Meta[String].timap(ProjectDescription.apply)(_.toString)
+  given Meta[ProjectName]        = Meta[String].timap(ProjectName.apply)(_.toString)
+  given Meta[ProjectOwnerId]     = Meta[UUID].timap(ProjectOwnerId.apply)(_.toUUID)
+  given Meta[ProjectOwnerName]   = Meta[String].timap(ProjectOwnerName.apply)(_.toString)
+
+  override def findProject(owner: ProjectOwner, name: ProjectName): F[Option[Project]] =
+    sql"""SELECT
+            "users".uid AS owner_id,
+            "users".name AS owner_name,
+            "users".email AS owner_email,
+            "projects".name,
+            "projects".description,
+            "projects".is_private
+          FROM "tickets"."projects" AS "projects"
+          JOIN "tickets"."users" AS "users"
+            ON "projects".owner = "users".uid
+          WHERE
+            "projects".owner = ${owner.uid}
+          AND
+            "projects".name = $name""".query[Project].option.transact(tx)
+
+  override def findProjectId(owner: ProjectOwner, name: ProjectName): F[Option[Long]] =
+    sql"""SELECT
+            "projects".id
+          FROM "tickets"."projects" AS "projects"
+          JOIN "tickets"."users" AS "users"
+            ON "projects".owner = "users".uid
+          WHERE
+            "projects".owner = ${owner.uid}
+          AND
+            "projects".name = $name""".query[Long].option.transact(tx)
+
+  override def findProjectOwner(name: ProjectOwnerName): F[Option[ProjectOwner]] =
+    sql"""SELECT
+            "users".uid,
+            "users".name,
+            "users".email
+          FROM "tickets"."users"
+          WHERE name = $name""".query[ProjectOwner].option.transact(tx)
+
+}