From 894778c67e985bd67ee2e3dbe809cc11b13b13a6 Mon Sep 17 00:00:00 2001 From: tksfz Date: Fri, 24 Nov 2023 19:49:13 -0800 Subject: [PATCH 1/6] Rename `healthckeck` directory to the intended `healthcheck` --- .../$package$/api/{healthckeck => healthcheck}/DbStatus.scala | 0 .../api/{healthckeck => healthcheck}/HealthCheckService.scala | 0 .../api/{healthckeck => healthcheck}/HealthCheckServiceLive.scala | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/main/g8/src/main/scala/$package$/api/{healthckeck => healthcheck}/DbStatus.scala (100%) rename src/main/g8/src/main/scala/$package$/api/{healthckeck => healthcheck}/HealthCheckService.scala (100%) rename src/main/g8/src/main/scala/$package$/api/{healthckeck => healthcheck}/HealthCheckServiceLive.scala (100%) diff --git a/src/main/g8/src/main/scala/$package$/api/healthckeck/DbStatus.scala b/src/main/g8/src/main/scala/$package$/api/healthcheck/DbStatus.scala similarity index 100% rename from src/main/g8/src/main/scala/$package$/api/healthckeck/DbStatus.scala rename to src/main/g8/src/main/scala/$package$/api/healthcheck/DbStatus.scala diff --git a/src/main/g8/src/main/scala/$package$/api/healthckeck/HealthCheckService.scala b/src/main/g8/src/main/scala/$package$/api/healthcheck/HealthCheckService.scala similarity index 100% rename from src/main/g8/src/main/scala/$package$/api/healthckeck/HealthCheckService.scala rename to src/main/g8/src/main/scala/$package$/api/healthcheck/HealthCheckService.scala diff --git a/src/main/g8/src/main/scala/$package$/api/healthckeck/HealthCheckServiceLive.scala b/src/main/g8/src/main/scala/$package$/api/healthcheck/HealthCheckServiceLive.scala similarity index 100% rename from src/main/g8/src/main/scala/$package$/api/healthckeck/HealthCheckServiceLive.scala rename to src/main/g8/src/main/scala/$package$/api/healthcheck/HealthCheckServiceLive.scala From 4620ee1b9443c23e5874ae364b2502322d611ce8 Mon Sep 17 00:00:00 2001 From: tksfz Date: Fri, 24 Nov 2023 19:52:06 -0800 Subject: [PATCH 2/6] Bump to scala 3.3.1 and zio-http 3.0.0-RC3 --- src/main/g8/build.sbt | 2 +- src/main/g8/default.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/g8/build.sbt b/src/main/g8/build.sbt index c1ead85..53077db 100644 --- a/src/main/g8/build.sbt +++ b/src/main/g8/build.sbt @@ -6,7 +6,7 @@ val logbackClassicVersion = "1.4.7" val postgresqlVersion = "42.6.0" val testContainersVersion = "0.40.15" val zioMockVersion = "1.0.0-RC11" -val zioHttpVersion = "3.0.0-RC1" +val zioHttpVersion = "3.0.0-RC3" val quillVersion = "4.6.0.1" lazy val root = (project in file(".")) diff --git a/src/main/g8/default.properties b/src/main/g8/default.properties index 7caa90e..5a9d7b6 100644 --- a/src/main/g8/default.properties +++ b/src/main/g8/default.properties @@ -1,5 +1,5 @@ name=zio-scala3-quickstart description=This is a seed project that creates Scala 3 based ZIO application. -scala_version=3.2.2 +scala_version=3.3.1 organization=com.example package=$organization$ From 0e71e8d3ced1c219c66bafa74a0fd08c99869c91 Mon Sep 17 00:00:00 2001 From: tksfz Date: Fri, 24 Nov 2023 19:52:26 -0800 Subject: [PATCH 3/6] Use status method --- src/main/g8/src/main/scala/$package$/api/Extensions.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/g8/src/main/scala/$package$/api/Extensions.scala b/src/main/g8/src/main/scala/$package$/api/Extensions.scala index fadfcd6..4a46bb5 100644 --- a/src/main/g8/src/main/scala/$package$/api/Extensions.scala +++ b/src/main/g8/src/main/scala/$package$/api/Extensions.scala @@ -20,7 +20,7 @@ private[api] object Extensions: def toResponseZIO(implicit ev: JsonEncoder[T]): UIO[Response] = toResponseZIO(Status.Ok) def toResponseZIO(status: Status)(implicit ev: JsonEncoder[T]): UIO[Response] = ZIO.succeed { - Response.json(data.toJson).withStatus(status) + Response.json(data.toJson).status(status) } def toEmptyResponseZIO: UIO[Response] = toEmptyResponseZIO(Status.NoContent) From 4adaf43ca9b534ee2dcd916f71c3dc5dbae7ce27 Mon Sep 17 00:00:00 2001 From: tksfz Date: Fri, 24 Nov 2023 19:52:43 -0800 Subject: [PATCH 4/6] HttpApp type signature only takes Env now --- .../g8/src/main/scala/$package$/api/HealthCheckRoutes.scala | 2 +- src/main/g8/src/main/scala/$package$/api/HttpRoutes.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/g8/src/main/scala/$package$/api/HealthCheckRoutes.scala b/src/main/g8/src/main/scala/$package$/api/HealthCheckRoutes.scala index 1bdce2a..a2d499d 100644 --- a/src/main/g8/src/main/scala/$package$/api/HealthCheckRoutes.scala +++ b/src/main/g8/src/main/scala/$package$/api/HealthCheckRoutes.scala @@ -6,7 +6,7 @@ import zio.http._ object HealthCheckRoutes: - val app: HttpApp[HealthCheckService, Nothing] = Http.collectZIO { + val app: HttpApp[HealthCheckService] = Http.collectZIO { case Method.HEAD -> !! / "healthcheck" => ZIO.succeed { diff --git a/src/main/g8/src/main/scala/$package$/api/HttpRoutes.scala b/src/main/g8/src/main/scala/$package$/api/HttpRoutes.scala index 29c58f6..fd93f83 100644 --- a/src/main/g8/src/main/scala/$package$/api/HttpRoutes.scala +++ b/src/main/g8/src/main/scala/$package$/api/HttpRoutes.scala @@ -11,7 +11,7 @@ import zio.json._ object HttpRoutes extends JsonSupport: - val app: HttpApp[ItemRepository, Nothing] = Http.collectZIO { + val app: HttpApp[ItemRepository] = Http.collectZIO { case Method.GET -> !! / "items" => val effect: ZIO[ItemRepository, DomainError, List[Item]] = ItemService.getAllItems() From 68cd1a5647676a137e31097b09a0ce381e77b2c5 Mon Sep 17 00:00:00 2001 From: tksfz Date: Fri, 24 Nov 2023 20:05:54 -0800 Subject: [PATCH 5/6] Use new Routes syntax --- .../$package$/api/HealthCheckRoutes.scala | 29 ++-- .../main/scala/$package$/api/HttpRoutes.scala | 149 ++++++++++-------- 2 files changed, 95 insertions(+), 83 deletions(-) diff --git a/src/main/g8/src/main/scala/$package$/api/HealthCheckRoutes.scala b/src/main/g8/src/main/scala/$package$/api/HealthCheckRoutes.scala index a2d499d..8cd2401 100644 --- a/src/main/g8/src/main/scala/$package$/api/HealthCheckRoutes.scala +++ b/src/main/g8/src/main/scala/$package$/api/HealthCheckRoutes.scala @@ -6,17 +6,20 @@ import zio.http._ object HealthCheckRoutes: - val app: HttpApp[HealthCheckService] = Http.collectZIO { + val app: HttpApp[HealthCheckService] = + Routes( + Method.HEAD / "healthcheck" -> + handler { + ZIO.succeed { + Response.status(Status.NoContent) + } + }, - case Method.HEAD -> !! / "healthcheck" => - ZIO.succeed { - Response.status(Status.NoContent) - } - - case Method.GET -> !! / "healthcheck" => - HealthCheckService.check.map { dbStatus => - if (dbStatus.status) Response.ok - else Response.status(Status.InternalServerError) - } - - } + Method.GET / "healthcheck" -> + handler { + HealthCheckService.check.map { dbStatus => + if (dbStatus.status) Response.ok + else Response.status(Status.InternalServerError) + } + } + ).toHttpApp diff --git a/src/main/g8/src/main/scala/$package$/api/HttpRoutes.scala b/src/main/g8/src/main/scala/$package$/api/HttpRoutes.scala index fd93f83..bed0983 100644 --- a/src/main/g8/src/main/scala/$package$/api/HttpRoutes.scala +++ b/src/main/g8/src/main/scala/$package$/api/HttpRoutes.scala @@ -11,73 +11,82 @@ import zio.json._ object HttpRoutes extends JsonSupport: - val app: HttpApp[ItemRepository] = Http.collectZIO { - case Method.GET -> !! / "items" => - val effect: ZIO[ItemRepository, DomainError, List[Item]] = - ItemService.getAllItems() - - effect.foldZIO(Utils.handleError, _.toResponseZIO) - - case Method.GET -> !! / "items" / itemId => - val effect: ZIO[ItemRepository, DomainError, Item] = - for { - id <- Utils.extractLong(itemId) - maybeItem <- ItemService.getItemById(ItemId(id)) - item <- maybeItem - .map(ZIO.succeed(_)) - .getOrElse(ZIO.fail(NotFoundError)) - } yield item - - effect.foldZIO(Utils.handleError, _.toResponseZIO) - - case Method.DELETE -> !! / "items" / itemId => - val effect: ZIO[ItemRepository, DomainError, Unit] = - for { - id <- Utils.extractLong(itemId) - amount <- ItemService.deleteItem(ItemId(id)) - _ <- if (amount == 0) ZIO.fail(NotFoundError) - else ZIO.unit - } yield () - - effect.foldZIO(Utils.handleError, _.toEmptyResponseZIO) - - case req @ Method.POST -> !! / "items" => - val effect: ZIO[ItemRepository, DomainError, Item] = - for { - createItem <- req.jsonBodyAs[CreateItemRequest] - itemId <- ItemService.addItem(createItem.name, createItem.price) - } yield Item(itemId, createItem.name, createItem.price) - - effect.foldZIO(Utils.handleError, _.toResponseZIO(Status.Created)) - - case req @ Method.PUT -> !! / "items" / itemId => - val effect: ZIO[ItemRepository, DomainError, Item] = - for { - id <- Utils.extractLong(itemId) - updateItem <- req.jsonBodyAs[UpdateItemRequest] - maybeItem <- ItemService.updateItem(ItemId(id), updateItem.name, updateItem.price) - item <- maybeItem - .map(ZIO.succeed(_)) - .getOrElse(ZIO.fail(NotFoundError)) - } yield item - - effect.foldZIO(Utils.handleError, _.toResponseZIO) - - case req @ Method.PATCH -> !! / "items" / itemId => - val effect: ZIO[ItemRepository, DomainError, Item] = - for { - id <- Utils.extractLong(itemId) - partialUpdateItem <- req.jsonBodyAs[PartialUpdateItemRequest] - maybeItem <- ItemService.partialUpdateItem( - id = ItemId(id), - name = partialUpdateItem.name, - price = partialUpdateItem.price, - ) - item <- maybeItem - .map(ZIO.succeed(_)) - .getOrElse(ZIO.fail(NotFoundError)) - } yield item - - effect.foldZIO(Utils.handleError, _.toResponseZIO) - - } + val app: HttpApp[ItemRepository] = + Routes( + Method.GET / "items" -> + handler { + val effect: ZIO[ItemRepository, DomainError, List[Item]] = + ItemService.getAllItems() + + effect.foldZIO(Utils.handleError, _.toResponseZIO) + }, + + Method.GET / "items" / long("itemId") -> + handler { (id: Long, req: Request) => + val effect: ZIO[ItemRepository, DomainError, Item] = + for { + maybeItem <- ItemService.getItemById(ItemId(id)) + item <- maybeItem + .map(ZIO.succeed(_)) + .getOrElse(ZIO.fail(NotFoundError)) + } yield item + + effect.foldZIO(Utils.handleError, _.toResponseZIO) + }, + + Method.DELETE / "items" / long("itemId") -> + handler { (id: Long, req: Request) => + val effect: ZIO[ItemRepository, DomainError, Unit] = + for { + amount <- ItemService.deleteItem(ItemId(id)) + _ <- if (amount == 0) ZIO.fail(NotFoundError) + else ZIO.unit + } yield () + + effect.foldZIO(Utils.handleError, _.toEmptyResponseZIO) + }, + + Method.POST / "items" -> + handler { (req: Request) => + val effect: ZIO[ItemRepository, DomainError, Item] = + for { + createItem <- req.jsonBodyAs[CreateItemRequest] + itemId <- ItemService.addItem(createItem.name, createItem.price) + } yield Item(itemId, createItem.name, createItem.price) + + effect.foldZIO(Utils.handleError, _.toResponseZIO(Status.Created)) + }, + + Method.PUT / "items" / long("itemId") -> + handler { (id: Long, req: Request) => + val effect: ZIO[ItemRepository, DomainError, Item] = + for { + updateItem <- req.jsonBodyAs[UpdateItemRequest] + maybeItem <- ItemService.updateItem(ItemId(id), updateItem.name, updateItem.price) + item <- maybeItem + .map(ZIO.succeed(_)) + .getOrElse(ZIO.fail(NotFoundError)) + } yield item + + effect.foldZIO(Utils.handleError, _.toResponseZIO) + }, + + Method.PATCH / "items" / long("itemId") -> + handler { (id: Long, req: Request) => + val effect: ZIO[ItemRepository, DomainError, Item] = + for { + partialUpdateItem <- req.jsonBodyAs[PartialUpdateItemRequest] + maybeItem <- ItemService.partialUpdateItem( + id = ItemId(id), + name = partialUpdateItem.name, + price = partialUpdateItem.price, + ) + item <- maybeItem + .map(ZIO.succeed(_)) + .getOrElse(ZIO.fail(NotFoundError)) + } yield item + + effect.foldZIO(Utils.handleError, _.toResponseZIO) + } + ).toHttpApp + From 3ac0fb72f64624b6a14d283cdbf1024e8d9b5086 Mon Sep 17 00:00:00 2001 From: tksfz Date: Fri, 24 Nov 2023 20:06:06 -0800 Subject: [PATCH 6/6] Replace !! with Root in test --- .../g8/src/test/scala/$package$/api/HealthCheckRoutes.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/g8/src/test/scala/$package$/api/HealthCheckRoutes.scala b/src/main/g8/src/test/scala/$package$/api/HealthCheckRoutes.scala index 7240f02..5bc5e56 100644 --- a/src/main/g8/src/test/scala/$package$/api/HealthCheckRoutes.scala +++ b/src/main/g8/src/test/scala/$package$/api/HealthCheckRoutes.scala @@ -11,7 +11,7 @@ object HealthCheckRoutesSpec extends ZIOSpecDefault: suite("health check")( test("ok status") { val actual = - HealthCheckRoutes.app.runZIO(Request.get(URL(!! / "healthcheck"))) + HealthCheckRoutes.app.runZIO(Request.get(URL(Root / "healthcheck"))) assertZIO(actual)(equalTo(Response(Status.Ok, Headers.empty, Body.empty))) } )