From 2449a392d63ff3015c70be1cefae6fafc572696b Mon Sep 17 00:00:00 2001 From: yoshinorin Date: Mon, 15 Jul 2024 11:27:38 +0900 Subject: [PATCH] refactor: use `cats.data.ContT` instead of `Action` --- .../yoshinorin/qualtet/actions/Action.scala | 18 ------- .../domains/archives/ArchiveService.scala | 11 ++-- .../domains/articles/ArticleService.scala | 29 +++++------ .../domains/authors/AuthorService.scala | 33 +++++++----- .../ContentSerializingService.scala | 51 +++++++++++-------- .../ContentTaggingService.scala | 41 +++++++++------ .../contentTypes/ContentTypeService.scala | 21 +++++--- .../domains/contents/ContentService.scala | 35 ++++++++----- .../ExternalResourceService.scala | 14 +++-- .../domains/robots/RobotsService.scala | 14 +++-- .../domains/search/SearchService.scala | 9 ++-- .../domains/series/SeriesService.scala | 21 +++++--- .../domains/sitemaps/SitemapService.scala | 9 ++-- .../qualtet/domains/tags/TagService.scala | 43 ++++++++++------ .../qualtet/infrastructure/db/Executer.scala | 6 +-- .../db/doobie/DoobieExecuter.scala | 12 ++--- 16 files changed, 209 insertions(+), 158 deletions(-) delete mode 100644 src/main/scala/net/yoshinorin/qualtet/actions/Action.scala diff --git a/src/main/scala/net/yoshinorin/qualtet/actions/Action.scala b/src/main/scala/net/yoshinorin/qualtet/actions/Action.scala deleted file mode 100644 index 6369dc57..00000000 --- a/src/main/scala/net/yoshinorin/qualtet/actions/Action.scala +++ /dev/null @@ -1,18 +0,0 @@ -package net.yoshinorin.qualtet.actions - -import cats.Monad - -sealed trait Action[R] -final case class Continue[F[_]: Monad, R, T](request: F[T], next: T => Action[R]) extends Action[R] -final case class Done[R](value: R) extends Action[R] - -object Action { - - def done[T]: T => Done[T] = { - val done: T => Done[T] = (rh: T) => { - Done(rh) - } - done - } - -} diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/archives/ArchiveService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/archives/ArchiveService.scala index 34d38333..51537f3d 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/archives/ArchiveService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/archives/ArchiveService.scala @@ -1,22 +1,23 @@ package net.yoshinorin.qualtet.domains.archives +import cats.Monad +import cats.data.ContT import cats.effect.IO -import net.yoshinorin.qualtet.actions.Action.* -import net.yoshinorin.qualtet.actions.{Action, Continue} import net.yoshinorin.qualtet.domains.contentTypes.ContentTypeService import net.yoshinorin.qualtet.message.Fail.NotFound import net.yoshinorin.qualtet.infrastructure.db.Executer import net.yoshinorin.qualtet.domains.contentTypes.ContentTypeId import net.yoshinorin.qualtet.syntax.* -import cats.Monad class ArchiveService[F[_]: Monad]( archiveRepository: ArchiveRepository[F], contentTypeService: ContentTypeService[F] )(using executer: Executer[F, IO]) { - def actions(contentTypeId: ContentTypeId): Action[Seq[ResponseArchive]] = { - Continue(archiveRepository.get(contentTypeId), Action.done[Seq[ResponseArchive]]) + def actions(contentTypeId: ContentTypeId): ContT[F, Seq[ResponseArchive], Seq[ResponseArchive]] = { + ContT.apply[F, Seq[ResponseArchive], Seq[ResponseArchive]] { next => + archiveRepository.get(contentTypeId) + } } def get: IO[Seq[ResponseArchive]] = { diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/articles/ArticleService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/articles/ArticleService.scala index eaf5eb8a..bee56559 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/articles/ArticleService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/articles/ArticleService.scala @@ -1,9 +1,8 @@ package net.yoshinorin.qualtet.domains.articles +import cats.data.ContT import cats.effect.IO import cats.Monad -import net.yoshinorin.qualtet.actions.Action.* -import net.yoshinorin.qualtet.actions.{Action, Continue} import net.yoshinorin.qualtet.domains.contentTypes.{ContentTypeId, ContentTypeService} import net.yoshinorin.qualtet.message.Fail.NotFound import net.yoshinorin.qualtet.domains.tags.TagName @@ -21,36 +20,36 @@ class ArticleService[F[_]: Monad]( contentTypeId: ContentTypeId, none: Unit = (), queryParams: ArticlesQueryParameter - ): Action[Seq[(Int, ResponseArticle)]] = { - Continue(articleRepository.getWithCount(contentTypeId, queryParams), Action.done[Seq[(Int, ResponseArticle)]]) + ): ContT[F, Seq[(Int, ResponseArticle)], Seq[(Int, ResponseArticle)]] = { + ContT.apply[F, Seq[(Int, ResponseArticle)], Seq[(Int, ResponseArticle)]] { next => + articleRepository.getWithCount(contentTypeId, queryParams) + } } def tagActions( contentTypeId: ContentTypeId, tagName: TagName, queryParams: ArticlesQueryParameter - ): Action[Seq[(Int, ResponseArticle)]] = { - Continue( - articleRepository.findByTagNameWithCount(contentTypeId, tagName, queryParams), - Action.done[Seq[(Int, ResponseArticle)]] - ) + ): ContT[F, Seq[(Int, ResponseArticle)], Seq[(Int, ResponseArticle)]] = { + ContT.apply[F, Seq[(Int, ResponseArticle)], Seq[(Int, ResponseArticle)]] { next => + articleRepository.findByTagNameWithCount(contentTypeId, tagName, queryParams) + } } def seriesActions( contentTypeId: ContentTypeId, seriesName: SeriesName, queryParams: ArticlesQueryParameter // TODO: `Optional` - ): Action[Seq[(Int, ResponseArticle)]] = { - Continue( - articleRepository.findBySeriesNameWithCount(contentTypeId, seriesName), - Action.done[Seq[(Int, ResponseArticle)]] - ) + ): ContT[F, Seq[(Int, ResponseArticle)], Seq[(Int, ResponseArticle)]] = { + ContT.apply[F, Seq[(Int, ResponseArticle)], Seq[(Int, ResponseArticle)]] { next => + articleRepository.findBySeriesNameWithCount(contentTypeId, seriesName) + } } def get[A]( data: A = (), queryParam: ArticlesQueryParameter - )(f: (ContentTypeId, A, ArticlesQueryParameter) => Action[Seq[(Int, ResponseArticle)]]): IO[ResponseArticleWithCount] = { + )(f: (ContentTypeId, A, ArticlesQueryParameter) => ContT[F, Seq[(Int, ResponseArticle)], Seq[(Int, ResponseArticle)]]): IO[ResponseArticleWithCount] = { for { c <- contentTypeService.findByName("article").throwIfNone(NotFound(detail = "content-type not found: article")) articlesWithCount <- executer.transact(f(c.id, data, queryParam)) diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/authors/AuthorService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/authors/AuthorService.scala index 745147f2..b7c76949 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/authors/AuthorService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/authors/AuthorService.scala @@ -1,35 +1,44 @@ package net.yoshinorin.qualtet.domains.authors +import cats.data.ContT import cats.effect.IO import cats.Monad import net.yoshinorin.qualtet.message.Fail.InternalServerError import net.yoshinorin.qualtet.infrastructure.db.Executer -import net.yoshinorin.qualtet.actions.Action.* -import net.yoshinorin.qualtet.actions.{Action, Continue} import net.yoshinorin.qualtet.syntax.* class AuthorService[F[_]: Monad]( authorRepository: AuthorRepository[F] )(using executer: Executer[F, IO]) { - def upsertActions(data: Author): Action[Int] = { - Continue(authorRepository.upsert(data), Action.done[Int]) + def upsertActions(data: Author): ContT[F, Int, Int] = { + ContT.apply[F, Int, Int] { next => + authorRepository.upsert(data) + } } - def fetchActions: Action[Seq[ResponseAuthor]] = { - Continue(authorRepository.getAll(), Action.done[Seq[ResponseAuthor]]) + def fetchActions: ContT[F, Seq[ResponseAuthor], Seq[ResponseAuthor]] = { + ContT.apply[F, Seq[ResponseAuthor], Seq[ResponseAuthor]] { next => + authorRepository.getAll() + } } - def findByIdActions(id: AuthorId): Action[Option[ResponseAuthor]] = { - Continue(authorRepository.findById(id), Action.done[Option[ResponseAuthor]]) + def findByIdActions(id: AuthorId): ContT[F, Option[ResponseAuthor], Option[ResponseAuthor]] = { + ContT.apply[F, Option[ResponseAuthor], Option[ResponseAuthor]] { next => + authorRepository.findById(id) + } } - def findByIdWithPasswordActions(id: AuthorId): Action[Option[Author]] = { - Continue(authorRepository.findByIdWithPassword(id), Action.done[Option[Author]]) + def findByIdWithPasswordActions(id: AuthorId): ContT[F, Option[Author], Option[Author]] = { + ContT.apply[F, Option[Author], Option[Author]] { next => + authorRepository.findByIdWithPassword(id) + } } - def findByNameActions(name: AuthorName): Action[Option[ResponseAuthor]] = { - Continue(authorRepository.findByName(name), Action.done[Option[ResponseAuthor]]) + def findByNameActions(name: AuthorName): ContT[F, Option[ResponseAuthor], Option[ResponseAuthor]] = { + ContT.apply[F, Option[ResponseAuthor], Option[ResponseAuthor]] { next => + authorRepository.findByName(name) + } } /** diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/contentSerializing/ContentSerializingService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/contentSerializing/ContentSerializingService.scala index 33064209..d0120986 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/contentSerializing/ContentSerializingService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/contentSerializing/ContentSerializingService.scala @@ -1,8 +1,7 @@ package net.yoshinorin.qualtet.domains.contentSerializing +import cats.data.ContT import cats.Monad -import net.yoshinorin.qualtet.actions.Action.* -import net.yoshinorin.qualtet.actions.{Action, Continue} import net.yoshinorin.qualtet.domains.contents.ContentId import net.yoshinorin.qualtet.domains.series.SeriesId @@ -10,39 +9,51 @@ class ContentSerializingService[F[_]: Monad]( contentSerializingRepository: ContentSerializingRepository[F] ) { - def findBySeriesIdActions(id: SeriesId): Action[Seq[ContentSerializing]] = { - Continue(contentSerializingRepository.findBySeriesId(id), Action.done[Seq[ContentSerializing]]) + def findBySeriesIdActions(id: SeriesId): ContT[F, Seq[ContentSerializing], Seq[ContentSerializing]] = { + ContT.apply[F, Seq[ContentSerializing], Seq[ContentSerializing]] { next => + contentSerializingRepository.findBySeriesId(id) + } } - def upsertActions(data: Option[ContentSerializing]): Action[Int] = { - data match { - case Some(d) => Continue(contentSerializingRepository.upsert(d), Action.done[Int]) - case None => Continue(contentSerializingRepository.fakeRequestInt, Action.done[Int]) + def upsertActions(data: Option[ContentSerializing]): ContT[F, Int, Int] = { + ContT.apply[F, Int, Int] { next => + data match { + case Some(d) => contentSerializingRepository.upsert(d) + case None => contentSerializingRepository.fakeRequestInt + } } } - def bulkUpsertActions(data: Option[List[ContentSerializing]]): Action[Int] = { - data match { - case Some(d) => Continue(contentSerializingRepository.bulkUpsert(d), Action.done[Int]) - case None => Continue(contentSerializingRepository.fakeRequestInt, Action.done[Int]) + def bulkUpsertActions(data: Option[List[ContentSerializing]]): ContT[F, Int, Int] = { + ContT.apply[F, Int, Int] { next => + data match { + case Some(d) => contentSerializingRepository.bulkUpsert(d) + case None => contentSerializingRepository.fakeRequestInt + } } } - def deleteBySeriesIdActions(id: SeriesId): Action[Unit] = { - Continue(contentSerializingRepository.deleteBySeriesId(id), Action.done[Unit]) + def deleteBySeriesIdActions(id: SeriesId): ContT[F, Unit, Unit] = { + ContT.apply[F, Unit, Unit] { next => + contentSerializingRepository.deleteBySeriesId(id) + } } - def deleteByContentIdActions(id: ContentId): Action[Unit] = { - Continue(contentSerializingRepository.deleteByContentId(id), Action.done[Unit]) + def deleteByContentIdActions(id: ContentId): ContT[F, Unit, Unit] = { + ContT.apply[F, Unit, Unit] { next => + contentSerializingRepository.deleteByContentId(id) + } } - def deleteActions(seriesId: SeriesId, contentIds: Seq[ContentId]): Action[Unit] = { - Continue(contentSerializingRepository.delete(seriesId, contentIds), Action.done[Unit]) + def deleteActions(seriesId: SeriesId, contentIds: Seq[ContentId]): ContT[F, Unit, Unit] = { + ContT.apply[F, Unit, Unit] { next => + contentSerializingRepository.delete(seriesId, contentIds) + } } - def bulkDeleteActions(data: (SeriesId, Seq[ContentId])): Action[Unit] = { + def bulkDeleteActions(data: (SeriesId, Seq[ContentId])): ContT[F, Unit, Unit] = { data._2.size match { - case 0 => Continue(contentSerializingRepository.fakeRequestUnit, Action.done[Unit]) + case 0 => ContT.apply[F, Unit, Unit] { next => contentSerializingRepository.fakeRequestUnit } case _ => this.deleteActions(data._1, data._2) } } diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/contentTaggings/ContentTaggingService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/contentTaggings/ContentTaggingService.scala index 6e807901..897a3598 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/contentTaggings/ContentTaggingService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/contentTaggings/ContentTaggingService.scala @@ -1,9 +1,8 @@ package net.yoshinorin.qualtet.domains.contentTaggings +import cats.data.ContT import cats.effect.IO import cats.Monad -import net.yoshinorin.qualtet.actions.Action.* -import net.yoshinorin.qualtet.actions.{Action, Continue} import net.yoshinorin.qualtet.domains.contents.ContentId import net.yoshinorin.qualtet.domains.tags.TagId import net.yoshinorin.qualtet.infrastructure.db.Executer @@ -12,32 +11,42 @@ class ContentTaggingService[F[_]: Monad]( contentTaggingRepository: ContentTaggingRepository[F] )(using executer: Executer[F, IO]) { - def findByTagIdActions(id: TagId): Action[Seq[ContentTagging]] = { - Continue(contentTaggingRepository.findByTagId(id), Action.done[Seq[ContentTagging]]) + def findByTagIdActions(id: TagId): ContT[F, Seq[ContentTagging], Seq[ContentTagging]] = { + ContT.apply[F, Seq[ContentTagging], Seq[ContentTagging]] { next => + contentTaggingRepository.findByTagId(id) + } } - def bulkUpsertActions(data: Option[List[ContentTagging]]): Action[Int] = { - data match { - case Some(d) => Continue(contentTaggingRepository.bulkUpsert(d), Action.done[Int]) - case None => Continue(contentTaggingRepository.fakeRequestInt, Action.done[Int]) + def bulkUpsertActions(data: Option[List[ContentTagging]]): ContT[F, Int, Int] = { + ContT.apply[F, Int, Int] { next => + data match { + case Some(d) => contentTaggingRepository.bulkUpsert(d) + case None => contentTaggingRepository.fakeRequestInt + } } } - def deleteByContentIdActions(id: ContentId): Action[Unit] = { - Continue(contentTaggingRepository.deleteByContentId(id), Action.done[Unit]) + def deleteByContentIdActions(id: ContentId): ContT[F, Unit, Unit] = { + ContT.apply[F, Unit, Unit] { next => + contentTaggingRepository.deleteByContentId(id) + } } - def deleteByTagIdActions(id: TagId): Action[Unit] = { - Continue(contentTaggingRepository.deleteByTagId(id), Action.done[Unit]) + def deleteByTagIdActions(id: TagId): ContT[F, Unit, Unit] = { + ContT.apply[F, Unit, Unit] { next => + contentTaggingRepository.deleteByTagId(id) + } } - def deleteActions(contentId: ContentId, tagIds: Seq[TagId]): Action[Unit] = { - Continue(contentTaggingRepository.delete(contentId, tagIds), Action.done[Unit]) + def deleteActions(contentId: ContentId, tagIds: Seq[TagId]): ContT[F, Unit, Unit] = { + ContT.apply[F, Unit, Unit] { next => + contentTaggingRepository.delete(contentId, tagIds) + } } - def bulkDeleteActions(data: (ContentId, Seq[TagId])): Action[Unit] = { + def bulkDeleteActions(data: (ContentId, Seq[TagId])): ContT[F, Unit, Unit] = { data._2.size match { - case 0 => Continue(contentTaggingRepository.fakeRequestUnit, Action.done[Unit]) + case 0 => ContT.apply[F, Unit, Unit] { next => contentTaggingRepository.fakeRequestUnit } case _ => this.deleteActions(data._1, data._2) } } diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/contentTypes/ContentTypeService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/contentTypes/ContentTypeService.scala index 4fbe5ab3..71675eca 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/contentTypes/ContentTypeService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/contentTypes/ContentTypeService.scala @@ -1,10 +1,9 @@ package net.yoshinorin.qualtet.domains.contentTypes +import cats.data.ContT import cats.effect.IO import cats.Monad import net.yoshinorin.qualtet.cache.CacheModule -import net.yoshinorin.qualtet.actions.Action.* -import net.yoshinorin.qualtet.actions.{Action, Continue} import net.yoshinorin.qualtet.message.Fail.InternalServerError import net.yoshinorin.qualtet.domains.Cacheable import net.yoshinorin.qualtet.infrastructure.db.Executer @@ -16,12 +15,16 @@ class ContentTypeService[F[_]: Monad]( )(using executer: Executer[F, IO]) extends Cacheable { - def upsertActions(data: ContentType): Action[Int] = { - Continue(contentRepository.upsert(data), Action.done[Int]) + def upsertActions(data: ContentType): ContT[F, Int, Int] = { + ContT.apply[F, Int, Int] { next => + contentRepository.upsert(data) + } } - def getAllActions: Action[Seq[ContentType]] = { - Continue(contentRepository.getAll(), Action.done[Seq[ContentType]]) + def getAllActions: ContT[F, Seq[ContentType], Seq[ContentType]] = { + ContT.apply[F, Seq[ContentType], Seq[ContentType]] { next => + contentRepository.getAll() + } } /** @@ -50,8 +53,10 @@ class ContentTypeService[F[_]: Monad]( */ def findByName(name: String): IO[Option[ContentType]] = { - def actions(name: String): Action[Option[ContentType]] = { - Continue(contentRepository.findByName(name), Action.done[Option[ContentType]]) + def actions(name: String): ContT[F, Option[ContentType], Option[ContentType]] = { + ContT.apply[F, Option[ContentType], Option[ContentType]] { next => + contentRepository.findByName(name) + } } def fromDB(name: String): IO[Option[ContentType]] = { diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/contents/ContentService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/contents/ContentService.scala index a5e96a13..da7a3d5e 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/contents/ContentService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/contents/ContentService.scala @@ -1,10 +1,9 @@ package net.yoshinorin.qualtet.domains.contents +import cats.data.ContT import cats.effect.IO import cats.Monad import cats.implicits.* -import net.yoshinorin.qualtet.actions.Action.* -import net.yoshinorin.qualtet.actions.{Action, Continue} import net.yoshinorin.qualtet.domains.authors.{AuthorName, AuthorService} import net.yoshinorin.qualtet.domains.contentSerializing.{ContentSerializing, ContentSerializingService} import net.yoshinorin.qualtet.domains.contentTypes.ContentTypeService @@ -31,24 +30,34 @@ class ContentService[F[_]: Monad]( executer: Executer[F, IO] ) { - def upsertActions(data: Content): Action[Int] = { - Continue(contentRepository.upsert(data), Action.done[Int]) + def upsertActions(data: Content): ContT[F, Int, Int] = { + ContT.apply[F, Int, Int] { next => + contentRepository.upsert(data) + } } - def deleteActions(id: ContentId): Action[Unit] = { - Continue(contentRepository.delete(id), Action.done[Unit]) + def deleteActions(id: ContentId): ContT[F, Unit, Unit] = { + ContT.apply[F, Unit, Unit] { next => + contentRepository.delete(id) + } } - def findByIdActions(id: ContentId): Action[Option[Content]] = { - Continue(contentRepository.findById(id), Action.done[Option[Content]]) + def findByIdActions(id: ContentId): ContT[F, Option[Content], Option[Content]] = { + ContT.apply[F, Option[Content], Option[Content]] { next => + contentRepository.findById(id) + } } - def findByPathActions(path: Path): Action[Option[Content]] = { - Continue(contentRepository.findByPath(path), Action.done[Option[Content]]) + def findByPathActions(path: Path): ContT[F, Option[Content], Option[Content]] = { + ContT.apply[F, Option[Content], Option[Content]] { next => + contentRepository.findByPath(path) + } } - def findByPathWithMetaActions(path: Path): Action[Option[ReadContentDbRow]] = { - Continue(contentRepository.findByPathWithMeta(path), Action.done[Option[ReadContentDbRow]]) + def findByPathWithMetaActions(path: Path): ContT[F, Option[ReadContentDbRow], Option[ReadContentDbRow]] = { + ContT.apply[F, Option[ReadContentDbRow], Option[ReadContentDbRow]] { next => + contentRepository.findByPathWithMeta(path) + } } /** @@ -207,7 +216,7 @@ class ContentService[F[_]: Monad]( executer.transact(findByIdActions(id)) } - def findBy[A](data: A)(f: A => Action[Option[ReadContentDbRow]]): IO[Option[ResponseContent]] = { + def findBy[A](data: A)(f: A => ContT[F, Option[ReadContentDbRow], Option[ReadContentDbRow]]): IO[Option[ResponseContent]] = { executer.transact(f(data)).flatMap { case None => IO(None) case Some(x) => diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/externalResources/ExternalResourceService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/externalResources/ExternalResourceService.scala index a4395b73..6eda634a 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/externalResources/ExternalResourceService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/externalResources/ExternalResourceService.scala @@ -1,19 +1,23 @@ package net.yoshinorin.qualtet.domains.externalResources +import cats.data.ContT import cats.Monad -import net.yoshinorin.qualtet.actions.{Action, Continue} import net.yoshinorin.qualtet.domains.contents.ContentId class ExternalResourceService[F[_]: Monad]( externalResourceRepository: ExternalResourceRepository[F] ) { - def bulkUpsertActions(data: List[ExternalResource]): Action[Int] = { - Continue(externalResourceRepository.bulkUpsert(data), Action.done[Int]) + def bulkUpsertActions(data: List[ExternalResource]): ContT[F, Int, Int] = { + ContT.apply[F, Int, Int] { next => + externalResourceRepository.bulkUpsert(data) + } } - def deleteActions(contentId: ContentId): Action[Unit] = { - Continue(externalResourceRepository.delete(contentId), Action.done[Unit]) + def deleteActions(contentId: ContentId): ContT[F, Unit, Unit] = { + ContT.apply[F, Unit, Unit] { next => + externalResourceRepository.delete(contentId) + } } } diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/robots/RobotsService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/robots/RobotsService.scala index 8483867e..b63da0ed 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/robots/RobotsService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/robots/RobotsService.scala @@ -1,17 +1,21 @@ package net.yoshinorin.qualtet.domains.robots +import cats.data.ContT import cats.Monad -import net.yoshinorin.qualtet.actions.{Action, Continue} import net.yoshinorin.qualtet.domains.contents.ContentId class RobotsService[F[_]: Monad]( robotsRepository: RobotsRepository[F] ) { - def upsertActions(data: Robots): Action[Int] = { - Continue(robotsRepository.upsert(data), Action.done[Int]) + def upsertActions(data: Robots): ContT[F, Int, Int] = { + ContT.apply[F, Int, Int] { next => + robotsRepository.upsert(data) + } } - def deleteActions(contentId: ContentId): Action[Unit] = { - Continue(robotsRepository.delete(contentId), Action.done[Unit]) + def deleteActions(contentId: ContentId): ContT[F, Unit, Unit] = { + ContT.apply[F, Unit, Unit] { next => + robotsRepository.delete(contentId) + } } } diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/search/SearchService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/search/SearchService.scala index f5390a28..34c20bde 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/search/SearchService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/search/SearchService.scala @@ -1,10 +1,9 @@ package net.yoshinorin.qualtet.domains.search +import cats.data.ContT import cats.effect.IO import cats.Monad import cats.implicits.* -import net.yoshinorin.qualtet.actions.Action.* -import net.yoshinorin.qualtet.actions.{Action, Continue} import net.yoshinorin.qualtet.config.SearchConfig import net.yoshinorin.qualtet.infrastructure.db.Executer import net.yoshinorin.qualtet.message.Fail.UnprocessableEntity @@ -19,8 +18,10 @@ class SearchService[F[_]: Monad]( searchRepository: SearchRepository[F] )(using executer: Executer[F, IO]) { - def actions(query: List[String]): Action[Seq[(Int, ResponseSearch)]] = { - Continue(searchRepository.search(query), Action.done[Seq[(Int, ResponseSearch)]]) + def actions(query: List[String]): ContT[F, Seq[(Int, ResponseSearch)], Seq[(Int, ResponseSearch)]] = { + ContT.apply[F, Seq[(Int, ResponseSearch)], Seq[(Int, ResponseSearch)]] { next => + searchRepository.search(query) + } } private[search] def extractQueryStringsFromQuery(query: Map[String, List[String]]): List[String] = query.getOrElse("q", List()).map(_.trim.toLower) diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/series/SeriesService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/series/SeriesService.scala index e9af9ea3..5794c7dd 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/series/SeriesService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/series/SeriesService.scala @@ -1,9 +1,8 @@ package net.yoshinorin.qualtet.domains.series +import cats.data.ContT import cats.effect.IO import cats.Monad -import net.yoshinorin.qualtet.actions.Action.* -import net.yoshinorin.qualtet.actions.{Action, Continue} import net.yoshinorin.qualtet.domains.articles.ArticleService import net.yoshinorin.qualtet.infrastructure.db.Executer import net.yoshinorin.qualtet.message.Fail.NotFound @@ -15,16 +14,22 @@ class SeriesService[F[_]: Monad]( articleService: ArticleService[F] )(using executer: Executer[F, IO]) { - def upsertActions(data: Series): Action[Int] = { - Continue(seriesRepository.upsert(data), Action.done[Int]) + def upsertActions(data: Series): ContT[F, Int, Int] = { + ContT.apply[F, Int, Int] { next => + seriesRepository.upsert(data) + } } - def findByNameActions(name: SeriesName): Action[Option[Series]] = { - Continue(seriesRepository.findByName(name), Action.done[Option[Series]]) + def findByNameActions(name: SeriesName): ContT[F, Option[Series], Option[Series]] = { + ContT.apply[F, Option[Series], Option[Series]] { next => + seriesRepository.findByName(name) + } } - def fetchActions: Action[Seq[Series]] = { - Continue(seriesRepository.getAll(), Action.done[Seq[Series]]) + def fetchActions: ContT[F, Seq[Series], Seq[Series]] = { + ContT.apply[F, Seq[Series], Seq[Series]] { next => + seriesRepository.getAll() + } } /** diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/sitemaps/SitemapService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/sitemaps/SitemapService.scala index f8ac0083..25061d70 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/sitemaps/SitemapService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/sitemaps/SitemapService.scala @@ -1,9 +1,8 @@ package net.yoshinorin.qualtet.domains.sitemaps +import cats.data.ContT import cats.effect.IO import cats.Monad -import net.yoshinorin.qualtet.actions.Action.* -import net.yoshinorin.qualtet.actions.{Action, Continue} import net.yoshinorin.qualtet.cache.CacheModule import net.yoshinorin.qualtet.infrastructure.db.Executer import net.yoshinorin.qualtet.domains.Cacheable @@ -16,8 +15,10 @@ class SitemapService[F[_]: Monad]( private val cacheKey = "sitemaps-full-cache" - def getActions: Action[Seq[Url]] = { - Continue(sitemapRepository.get(), Action.done[Seq[Url]]) + def getActions: ContT[F, Seq[Url], Seq[Url]] = { + ContT.apply[F, Seq[Url], Seq[Url]] { next => + sitemapRepository.get() + } } def get(): IO[Seq[Url]] = { diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/tags/TagService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/tags/TagService.scala index fe3736f1..b97501a6 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/tags/TagService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/tags/TagService.scala @@ -1,10 +1,9 @@ package net.yoshinorin.qualtet.domains.tags +import cats.data.ContT import cats.effect.IO import cats.Monad import cats.implicits.* -import net.yoshinorin.qualtet.actions.Action.* -import net.yoshinorin.qualtet.actions.{Action, Continue} import net.yoshinorin.qualtet.domains.contentTaggings.ContentTaggingService import net.yoshinorin.qualtet.infrastructure.db.Executer import net.yoshinorin.qualtet.message.Fail.NotFound @@ -16,31 +15,43 @@ class TagService[F[_]: Monad]( contentTaggingService: ContentTaggingService[F] )(using executer: Executer[F, IO]) { - def bulkUpsertActions(data: Option[List[Tag]]): Action[Int] = { - data match { - case Some(d) => Continue(tagRepository.bulkUpsert(d), Action.done[Int]) - case None => Continue(tagRepository.fakeRequest(), Action.done[Int]) + def bulkUpsertActions(data: Option[List[Tag]]): ContT[F, Int, Int] = { + ContT.apply[F, Int, Int] { next => + data match { + case Some(d) => tagRepository.bulkUpsert(d) + case None => tagRepository.fakeRequest() + } } } - def getAllActions: Action[Seq[ResponseTag]] = { - Continue(tagRepository.getAll(), Action.done[Seq[ResponseTag]]) + def getAllActions: ContT[F, Seq[ResponseTag], Seq[ResponseTag]] = { + ContT.apply[F, Seq[ResponseTag], Seq[ResponseTag]] { next => + tagRepository.getAll() + } } - def findByIdActions(id: TagId): Action[Option[Tag]] = { - Continue(tagRepository.findById(id), Action.done[Option[Tag]]) + def findByIdActions(id: TagId): ContT[F, Option[Tag], Option[Tag]] = { + ContT.apply[F, Option[Tag], Option[Tag]] { next => + tagRepository.findById(id) + } } - def findByNameActions(tagName: TagName): Action[Option[Tag]] = { - Continue(tagRepository.findByName(tagName), Action.done[Option[Tag]]) + def findByNameActions(tagName: TagName): ContT[F, Option[Tag], Option[Tag]] = { + ContT.apply[F, Option[Tag], Option[Tag]] { next => + tagRepository.findByName(tagName) + } } - def findByContentIdActions(contenId: ContentId): Action[Seq[Tag]] = { - Continue(tagRepository.findByContentId(contenId), Action.done[Seq[Tag]]) + def findByContentIdActions(contenId: ContentId): ContT[F, Seq[Tag], Seq[Tag]] = { + ContT.apply[F, Seq[Tag], Seq[Tag]] { next => + tagRepository.findByContentId(contenId) + } } - def deleteActions(id: TagId): Action[Unit] = { - Continue(tagRepository.delete(id), Action.done[Unit]) + def deleteActions(id: TagId): ContT[F, Unit, Unit] = { + ContT.apply[F, Unit, Unit] { next => + tagRepository.delete(id) + } } /** diff --git a/src/main/scala/net/yoshinorin/qualtet/infrastructure/db/Executer.scala b/src/main/scala/net/yoshinorin/qualtet/infrastructure/db/Executer.scala index 59ea6d6a..469edf41 100644 --- a/src/main/scala/net/yoshinorin/qualtet/infrastructure/db/Executer.scala +++ b/src/main/scala/net/yoshinorin/qualtet/infrastructure/db/Executer.scala @@ -1,12 +1,12 @@ package net.yoshinorin.qualtet.infrastructure.db import cats.Monad -import net.yoshinorin.qualtet.actions.Action +import cats.data.ContT trait Executer[F[_], G[_]: Monad] { - def perform[R](a: Action[R]): F[R] + def perform[R](a: ContT[F, R, R]): F[R] + def transact[R](t: ContT[F, R, R]): G[R] def transact[T](t: F[T]): G[T] - def transact[R](a: Action[R]): G[R] def transact2[T1, T2](ts: (F[(T1, T2)])): G[T2] def transact4[T1, T2, T3, T4](ts: (F[(T1, T2, T3, T4)])): G[T4] def transact8[T1, T2, T3, T4, T5, T6, T7, T8](ts: (F[(T1, T2, T3, T4, T5, T6, T7, T8)])): G[T8] diff --git a/src/main/scala/net/yoshinorin/qualtet/infrastructure/db/doobie/DoobieExecuter.scala b/src/main/scala/net/yoshinorin/qualtet/infrastructure/db/doobie/DoobieExecuter.scala index 856a3a6c..4b4471ef 100644 --- a/src/main/scala/net/yoshinorin/qualtet/infrastructure/db/doobie/DoobieExecuter.scala +++ b/src/main/scala/net/yoshinorin/qualtet/infrastructure/db/doobie/DoobieExecuter.scala @@ -1,12 +1,13 @@ package net.yoshinorin.qualtet.infrastructure.db.doobie +import cats.data.ContT import cats.implicits.catsSyntaxApplicativeId import doobie.* import doobie.implicits.* import cats.effect.* import cats.effect.IO -import net.yoshinorin.qualtet.actions.{Action, Continue, Done} import net.yoshinorin.qualtet.infrastructure.db.Executer +import doobie.free.connection.ConnectionIO class DoobieExecuter(tx: Transactor[IO]) extends Executer[ConnectionIO, IO] { @@ -15,14 +16,13 @@ class DoobieExecuter(tx: Transactor[IO]) extends Executer[ConnectionIO, IO] { // val executors: ExecutorService = Executors.newCachedThreadPool() // val executionContexts: ExecutionContextExecutor = scala.concurrent.ExecutionContext.fromExecutor(executors) - override def perform[R](action: Action[R]): ConnectionIO[R] = action match { - case continue: Continue[ConnectionIO, R, _] => continue.request.flatMap { t => perform(continue.next(t)) } - case done: Done[R] => done.value.pure[ConnectionIO] + override def perform[R](c: ContT[doobie.ConnectionIO, R, R]): ConnectionIO[R] = { + c.run { x => x.pure[ConnectionIO] } } - override def transact[T](connectionIO: ConnectionIO[T]): IO[T] = connectionIO.transact(tx) + override def transact[R](t: ContT[doobie.ConnectionIO, R, R]): IO[R] = transact(t.run { x => x.pure[ConnectionIO] }) - override def transact[R](action: Action[R]): IO[R] = transact(perform(action)) + override def transact[T](connectionIO: ConnectionIO[T]): IO[T] = connectionIO.transact(tx) override def transact2[T1, T2](ts: (ConnectionIO[(T1, T2)])): IO[T2] = { for {