Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/g8/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -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("."))
Expand Down
2 changes: 1 addition & 1 deletion src/main/g8/default.properties
Original file line number Diff line number Diff line change
@@ -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$
2 changes: 1 addition & 1 deletion src/main/g8/src/main/scala/$package$/api/Extensions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
29 changes: 16 additions & 13 deletions src/main/g8/src/main/scala/$package$/api/HealthCheckRoutes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ import zio.http._

object HealthCheckRoutes:

val app: HttpApp[HealthCheckService, Nothing] = 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
149 changes: 79 additions & 70 deletions src/main/g8/src/main/scala/$package$/api/HttpRoutes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,73 +11,82 @@ import zio.json._

object HttpRoutes extends JsonSupport:

val app: HttpApp[ItemRepository, Nothing] = 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

Original file line number Diff line number Diff line change
Expand Up @@ -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)))
}
)
Expand Down