From cc5b30e6eaa42ed2b598d42f42dd6df25c937733 Mon Sep 17 00:00:00 2001 From: Milad Khajavi Date: Sun, 28 Apr 2024 19:42:39 +0330 Subject: [PATCH 1/6] update --- zio-quickstart-restful-webservice/build.sbt | 5 +- .../scala/dev/zio/quickstart/MainApp.scala | 15 +++--- .../{CounterApp.scala => CounterRoutes.scala} | 25 +++++---- ...DownloadApp.scala => DownloadRoutes.scala} | 22 ++++---- .../zio/quickstart/greet/GreetingApp.scala | 27 ---------- .../zio/quickstart/greet/GreetingRoutes.scala | 29 ++++++++++ .../scala/dev/zio/quickstart/users/User.scala | 7 ++- .../dev/zio/quickstart/users/UserApp.scala | 46 ---------------- .../dev/zio/quickstart/users/UserRoutes.scala | 53 +++++++++++++++++++ 9 files changed, 120 insertions(+), 109 deletions(-) rename zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/counter/{CounterApp.scala => CounterRoutes.scala} (57%) rename zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/download/{DownloadApp.scala => DownloadRoutes.scala} (71%) delete mode 100644 zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala create mode 100644 zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala delete mode 100644 zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/UserApp.scala create mode 100644 zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala diff --git a/zio-quickstart-restful-webservice/build.sbt b/zio-quickstart-restful-webservice/build.sbt index 179ff89..9cc1171 100644 --- a/zio-quickstart-restful-webservice/build.sbt +++ b/zio-quickstart-restful-webservice/build.sbt @@ -1,9 +1,10 @@ scalaVersion := "3.3.1" +resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots" libraryDependencies ++= Seq( - "dev.zio" %% "zio" % "2.0.19", + "dev.zio" %% "zio" % "2.0.22", "dev.zio" %% "zio-json" % "0.6.2", - "dev.zio" %% "zio-http" % "3.0.0-RC2", + "dev.zio" %% "zio-http" % "3.0.0-RC6+36-d283e073-SNAPSHOT", "io.getquill" %% "quill-zio" % "4.7.0", "io.getquill" %% "quill-jdbc-zio" % "4.7.0", "com.h2database" % "h2" % "2.2.224" diff --git a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/MainApp.scala b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/MainApp.scala index ad8814e..8d8f0c1 100644 --- a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/MainApp.scala +++ b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/MainApp.scala @@ -1,19 +1,16 @@ package dev.zio.quickstart -import dev.zio.quickstart.counter.CounterApp -import dev.zio.quickstart.download.DownloadApp -import dev.zio.quickstart.greet.GreetingApp -import dev.zio.quickstart.users.{InmemoryUserRepo, PersistentUserRepo, UserApp} +import dev.zio.quickstart.counter.CounterRoutes +import dev.zio.quickstart.download.DownloadRoutes +import dev.zio.quickstart.greet.GreetingRoutes +import dev.zio.quickstart.users.{InmemoryUserRepo, PersistentUserRepo, UserRoutes} import zio._ import zio.http._ object MainApp extends ZIOAppDefault: - def run: ZIO[Environment with ZIOAppArgs with Scope, Throwable, Any] = - val httpApps = GreetingApp() ++ DownloadApp() ++ CounterApp() ++ UserApp() + def run = Server - .serve( - httpApps.withDefaultErrorResponse - ) + .serve(GreetingRoutes() ++ DownloadRoutes() ++ CounterRoutes() ++ UserRoutes()) .provide( Server.defaultWithPort(8080), diff --git a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/counter/CounterRoutes.scala similarity index 57% rename from zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala rename to zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/counter/CounterRoutes.scala index 3af2827..1309c20 100644 --- a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala +++ b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/counter/CounterRoutes.scala @@ -3,30 +3,33 @@ package dev.zio.quickstart.counter import zio.http._ import zio.{Ref, ZIO, ZLayer} -/** An http app that: - * - Accepts `Request` and returns a `Response` - * - Does not fail - * - Requires the `Ref[Int]` as the environment +/** Collection of routes that: + * - Accept `Request` and returns a `Response` + * - Do not fail + * - Require the `Ref[Int]` as the environment */ -object CounterApp: - def apply(): Http[Ref[Int], Nothing, Request, Response] = - Http.collectZIO[Request] { - case Method.GET -> Root / "up" => +object CounterRoutes: + def apply(): Routes[Ref[Int], Nothing] = + Routes( + Method.GET / "up" -> handler { ZIO.serviceWithZIO[Ref[Int]] { ref => ref .updateAndGet(_ + 1) .map(_.toString) .map(Response.text) } - case Method.GET -> Root / "down" => + }, + Method.GET / "down" -> handler { ZIO.serviceWithZIO[Ref[Int]] { ref => ref .updateAndGet(_ - 1) .map(_.toString) .map(Response.text) } - case Method.GET -> Root / "get" => + }, + Method.GET / "get" -> handler { ZIO.serviceWithZIO[Ref[Int]](ref => ref.get.map(_.toString).map(Response.text) ) - } + } + ) diff --git a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/download/DownloadRoutes.scala similarity index 71% rename from zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala rename to zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/download/DownloadRoutes.scala index 2d719bc..fc1dacf 100644 --- a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala +++ b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/download/DownloadRoutes.scala @@ -3,17 +3,17 @@ package dev.zio.quickstart.download import zio._ import zio.http._ import zio.stream.ZStream +import zio.schema.codec.JsonCodec.zioJsonBinaryCodec -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - May fail with type of `Throwable` - * - Does not require any environment +/** Collection of routes that: + * - Accept a `Request` and returns a `Response` + * - Do not require any environment */ -object DownloadApp: - def apply(): Http[Any, Throwable, Request, Response] = - Http.collect[Request] { +object DownloadRoutes: + def apply(): Routes[Any, Nothing] = + Routes( // GET /download - case Method.GET -> Root / "download" => + Method.GET / Root / "download" -> handler { val fileName = "file.txt" http.Response( status = Status.Ok, @@ -23,10 +23,11 @@ object DownloadApp: ), body = Body.fromStream(ZStream.fromResource(fileName)) ) + }, // Download a large file using streams // GET /download/stream - case Method.GET -> Root / "download" / "stream" => + Method.GET / "download" / "stream" -> handler { val file = "bigfile.txt" http.Response( status = Status.Ok, @@ -40,4 +41,5 @@ object DownloadApp: .schedule(Schedule.spaced(50.millis)) ) ) - } + } + ) diff --git a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala deleted file mode 100644 index 3466ac5..0000000 --- a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala +++ /dev/null @@ -1,27 +0,0 @@ -package dev.zio.quickstart.greet - -import zio.http._ - -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - Does not fail - * - Does not use the environment - */ -object GreetingApp: - def apply(): Http[Any, Nothing, Request, Response] = - Http.collect[Request] { - // GET /greet?name=:name - case req @ (Method.GET -> Root / "greet") - if (req.url.queryParams.nonEmpty) => - Response.text( - s"Hello ${req.url.queryParams.get("name").map(_.mkString(" and "))}!" - ) - - // GET /greet - case Method.GET -> Root / "greet" => - Response.text(s"Hello World!") - - // GET /greet/:name - case Method.GET -> Root / "greet" / name => - Response.text(s"Hello $name!") - } diff --git a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala new file mode 100644 index 0000000..c6c1124 --- /dev/null +++ b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala @@ -0,0 +1,29 @@ +package dev.zio.quickstart.greet + +import zio.http.* + +/** Collection of routes that: + * - Accept a `Request` and return a `Response` + * - Do not fail + * - Do not use the environment + */ +object GreetingRoutes: + def apply(): Routes[Any, Nothing] = + Routes( + // GET /greet?name=:name + Method.GET / "greet" -> handler { (req: Request) => + if (req.url.queryParams.nonEmpty) + Response.text( + s"Hello ${req.url.queryParams("name").map(_.mkString(" and "))}!" + ) + else Response.badRequest("The name query parameter is missing!") + }, + + // GET /greet + Method.GET / "greet" -> handler(Response.text(s"Hello World!")), + + // GET /greet/:name + Method.GET / "greet" / string("name") -> handler {( name: String, _: Request) => + Response.text(s"Hello $name!") + } + ) diff --git a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/User.scala b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/User.scala index 54044eb..0091629 100644 --- a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/User.scala +++ b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/User.scala @@ -2,11 +2,10 @@ package dev.zio.quickstart.users import java.util.UUID import zio.json.* +import zio.schema._ +import zio.schema.DeriveSchema._ case class User(name: String, age: Int) object User: - given JsonEncoder[User] = - DeriveJsonEncoder.gen[User] - given JsonDecoder[User] = - DeriveJsonDecoder.gen[User] + given Schema[User] = DeriveSchema.gen[User] \ No newline at end of file diff --git a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/UserApp.scala b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/UserApp.scala deleted file mode 100644 index 1812d80..0000000 --- a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/UserApp.scala +++ /dev/null @@ -1,46 +0,0 @@ -package dev.zio.quickstart.users - -import zio._ -import zio.http._ -import zio.json._ - -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - May fail with type of `Throwable` - * - Uses a `UserRepo` as the environment - */ -object UserApp: - def apply(): Http[UserRepo, Throwable, Request, Response] = - Http.collectZIO[Request] { - // POST /users -d '{"name": "John", "age": 35}' - case req @ (Method.POST -> Root / "users") => - (for { - u <- req.body.asString.map(_.fromJson[User]) - r <- u match - case Left(e) => - ZIO - .debug(s"Failed to parse the input: $e") - .as( - Response.text(e).withStatus(Status.BadRequest) - ) - case Right(u) => - UserRepo - .register(u) - .map(id => Response.text(id)) - } yield r).orDie - - // GET /users/:id - case Method.GET -> Root / "users" / id => - UserRepo - .lookup(id) - .map { - case Some(user) => - Response.json(user.toJson) - case None => - Response.status(Status.NotFound) - } - .orDie - // GET /users - case Method.GET -> Root / "users" => - UserRepo.users.map(response => Response.json(response.toJson)).orDie - } diff --git a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala new file mode 100644 index 0000000..cfce522 --- /dev/null +++ b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala @@ -0,0 +1,53 @@ +package dev.zio.quickstart.users + +import zio.* +import zio.http.* +import zio.schema.codec.JsonCodec.schemaBasedBinaryCodec + +/** Collection of routes that: + * - Accept a `Request` and returns a `Response` + * - May fail with type of `Response` + * - Require a `UserRepo` from the environment + */ +object UserRoutes: + def apply(): Routes[UserRepo, Response] = + Routes( + // POST /users -d '{"name": "John", "age": 35}' + Method.POST / "users" -> handler { (req: Request) => + for { + u <- req.body.to[User].orElseFail(Response.badRequest) + r <- + UserRepo + .register(u) + .mapBoth( + _ => + Response + .internalServerError(s"Failed to register the user: $u"), + id => Response.text(id) + ) + } yield r + }, + + // GET /users/:id + Method.GET / "users" / string("id") -> handler { + (id: String, _: Request) => + UserRepo + .lookup(id) + .mapBoth( + _ => Response.internalServerError(s"Cannot retrieve user $id"), + { + case Some(user) => + Response(body = Body.from(user)) + case None => + Response.notFound(s"User $id not found!") + } + ) + }, + // GET /users + Method.GET / "users" -> handler { + UserRepo.users.mapBoth( + _ => Response.internalServerError("Cannot retrieve users!"), + users => Response(body =Body.from(users)) + ) + } + ) From d90d35e4af5453a7496077f72367cb5aceb5a80c Mon Sep 17 00:00:00 2001 From: Milad Khajavi Date: Sun, 28 Apr 2024 20:12:37 +0330 Subject: [PATCH 2/6] update zio-quickstart-restful-webservice-configurable-app --- .../build.sbt | 4 +- .../scala/dev/zio/quickstart/MainApp.scala | 11 ++-- .../{CounterApp.scala => CounterRoutes.scala} | 30 ++++++----- ...DownloadApp.scala => DownloadRoutes.scala} | 24 +++++---- .../zio/quickstart/greet/GreetingApp.scala | 28 ---------- .../zio/quickstart/greet/GreetingRoutes.scala | 30 +++++++++++ .../scala/dev/zio/quickstart/users/User.scala | 9 ++-- .../dev/zio/quickstart/users/UserApp.scala | 49 ----------------- .../dev/zio/quickstart/users/UserRoutes.scala | 54 +++++++++++++++++++ 9 files changed, 123 insertions(+), 116 deletions(-) rename zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/counter/{CounterApp.scala => CounterRoutes.scala} (54%) rename zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/download/{DownloadApp.scala => DownloadRoutes.scala} (71%) delete mode 100644 zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala create mode 100644 zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala delete mode 100644 zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/UserApp.scala create mode 100644 zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala diff --git a/zio-quickstart-restful-webservice-configurable-app/build.sbt b/zio-quickstart-restful-webservice-configurable-app/build.sbt index 8d0701a..f3dbaa0 100644 --- a/zio-quickstart-restful-webservice-configurable-app/build.sbt +++ b/zio-quickstart-restful-webservice-configurable-app/build.sbt @@ -3,7 +3,7 @@ scalaVersion := "2.13.8" libraryDependencies ++= Seq( "dev.zio" %% "zio" % "2.0.19", "dev.zio" %% "zio-json" % "0.6.2", - "dev.zio" %% "zio-http" % "3.0.0-RC2", + "dev.zio" %% "zio-http" % "3.0.0-RC6+36-d283e073-SNAPSHOT", "io.getquill" %% "quill-zio" % "4.7.0", "io.getquill" %% "quill-jdbc-zio" % "4.7.0", "com.h2database" % "h2" % "2.2.224", @@ -12,4 +12,4 @@ libraryDependencies ++= Seq( "dev.zio" %% "zio-config-magnolia" % "4.0.0-RC16" ) -resolvers += Resolver.sonatypeRepo("public") +resolvers += Resolver.sonatypeOssRepos("snapshots") diff --git a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/MainApp.scala b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/MainApp.scala index 2d51fa3..99315e8 100644 --- a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/MainApp.scala +++ b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/MainApp.scala @@ -1,10 +1,10 @@ package dev.zio.quickstart import dev.zio.quickstart.config.HttpServerConfig -import dev.zio.quickstart.counter.CounterApp -import dev.zio.quickstart.download.DownloadApp -import dev.zio.quickstart.greet.GreetingApp -import dev.zio.quickstart.users.{InmemoryUserRepo, UserApp, UserRepo} +import dev.zio.quickstart.counter.CounterRoutes +import dev.zio.quickstart.download.DownloadRoutes +import dev.zio.quickstart.greet.GreetingRoutes +import dev.zio.quickstart.users.{InmemoryUserRepo, UserRoutes} import zio._ import zio.config.typesafe.TypesafeConfigProvider import zio.http._ @@ -34,9 +34,8 @@ object MainApp extends ZIOAppDefault { ) def run = { - val httpApp = GreetingApp() ++ DownloadApp() ++ CounterApp() ++ UserApp() (Server - .serve(httpApp.withDefaultErrorResponse) + .install(GreetingRoutes() ++ DownloadRoutes() ++ CounterRoutes() ++ UserRoutes()) .flatMap(port => Console.printLine(s"Started server on port: $port") ) *> ZIO.never) diff --git a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/counter/CounterRoutes.scala similarity index 54% rename from zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala rename to zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/counter/CounterRoutes.scala index 3d18a07..8d0ea70 100644 --- a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala +++ b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/counter/CounterRoutes.scala @@ -1,34 +1,36 @@ package dev.zio.quickstart.counter import zio.http._ -import zio.{Ref, ZIO} +import zio._ -/** An http app that: - * - Accepts `Request` and returns a `Response` - * - Does not fail - * - Requires the `Ref[Int]` as the environment +/** Collection of routes that: + * - Accept `Request` and returns a `Response` + * - Do not fail + * - Require the `Ref[Int]` as the environment */ -object CounterApp { - def apply(): Http[Ref[Int], Nothing, Request, Response] = { - Http.collectZIO[Request] { - case Method.GET -> Root / "up" => +object CounterRoutes { + def apply(): Routes[Ref[Int], Nothing] = + Routes( + Method.GET / "up" -> handler { ZIO.serviceWithZIO[Ref[Int]] { ref => ref .updateAndGet(_ + 1) .map(_.toString) .map(Response.text) } - case Method.GET -> Root / "down" => + }, + Method.GET / "down" -> handler { ZIO.serviceWithZIO[Ref[Int]] { ref => ref .updateAndGet(_ - 1) .map(_.toString) .map(Response.text) } - case Method.GET -> Root / "get" => + }, + Method.GET / "get" -> handler { ZIO.serviceWithZIO[Ref[Int]](ref => ref.get.map(_.toString).map(Response.text) ) - } - } -} + } + ) +} \ No newline at end of file diff --git a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/download/DownloadRoutes.scala similarity index 71% rename from zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala rename to zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/download/DownloadRoutes.scala index 89a8c5a..df5a5b2 100644 --- a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala +++ b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/download/DownloadRoutes.scala @@ -3,17 +3,17 @@ package dev.zio.quickstart.download import zio._ import zio.http._ import zio.stream.ZStream +import zio.schema.codec.JsonCodec.zioJsonBinaryCodec -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - May fail with type of `Throwable` - * - Does not require any environment +/** Collection of routes that: + * - Accept a `Request` and returns a `Response` + * - Do not require any environment */ -object DownloadApp { - def apply() = - Http.collect[Request] { +object DownloadRoutes { + def apply(): Routes[Any, Nothing] = + Routes( // GET /download - case Method.GET -> Root / "download" => + Method.GET / Root / "download" -> handler { val fileName = "file.txt" http.Response( status = Status.Ok, @@ -23,10 +23,11 @@ object DownloadApp { ), body = Body.fromStream(ZStream.fromResource(fileName)) ) + }, // Download a large file using streams // GET /download/stream - case Method.GET -> Root / "download" / "stream" => + Method.GET / "download" / "stream" -> handler { val file = "bigfile.txt" http.Response( status = Status.Ok, @@ -40,5 +41,6 @@ object DownloadApp { .schedule(Schedule.spaced(50.millis)) ) ) - } -} + } + ) +} \ No newline at end of file diff --git a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala deleted file mode 100644 index 2247925..0000000 --- a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala +++ /dev/null @@ -1,28 +0,0 @@ -package dev.zio.quickstart.greet - -import zio.http._ - -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - Does not fail - * - Does not use the environment - */ -object GreetingApp { - def apply(): Http[Any, Nothing, Request, Response] = - Http.collect[Request] { - // GET /greet?name=:name - case req @ (Method.GET -> Root / "greet") - if (req.url.queryParams.nonEmpty) => - Response.text( - s"Hello ${req.url.queryParams.get("name").map(_.mkString(" and "))}!" - ) - - // GET /greet - case Method.GET -> Root / "greet" => - Response.text(s"Hello World!") - - // GET /greet/:name - case Method.GET -> Root / "greet" / name => - Response.text(s"Hello $name!") - } -} diff --git a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala new file mode 100644 index 0000000..5f0b5e3 --- /dev/null +++ b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala @@ -0,0 +1,30 @@ +package dev.zio.quickstart.greet + +import zio.http._ + +/** Collection of routes that: + * - Accept a `Request` and return a `Response` + * - Do not fail + * - Do not use the environment + */ +object GreetingRoutes { + def apply(): Routes[Any, Nothing] = + Routes( + // GET /greet?name=:name + Method.GET / "greet" -> handler { (req: Request) => + if (req.url.queryParams.nonEmpty) + Response.text( + s"Hello ${req.url.queryParams("name").map(_.mkString(" and "))}!" + ) + else Response.badRequest("The name query parameter is missing!") + }, + + // GET /greet + Method.GET / "greet" -> handler(Response.text(s"Hello World!")), + + // GET /greet/:name + Method.GET / "greet" / string("name") -> handler { (name: String, _: Request) => + Response.text(s"Hello $name!") + } + ) +} \ No newline at end of file diff --git a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/User.scala b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/User.scala index 32c0a16..1cdb162 100644 --- a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/User.scala +++ b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/User.scala @@ -1,13 +1,10 @@ package dev.zio.quickstart.users -import java.util.UUID -import zio.json._ +import zio.schema.{DeriveSchema, Schema} case class User(name: String, age: Int) object User { - implicit val encoder: JsonEncoder[User] = - DeriveJsonEncoder.gen[User] - implicit val decoder: JsonDecoder[User] = - DeriveJsonDecoder.gen[User] + implicit val schema: Schema[User] = + DeriveSchema.gen[User] } diff --git a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/UserApp.scala b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/UserApp.scala deleted file mode 100644 index f72db1e..0000000 --- a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/UserApp.scala +++ /dev/null @@ -1,49 +0,0 @@ -package dev.zio.quickstart.users - -import zio._ -import zio.http._ -import zio.json._ - -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - May fail with type of `Throwable` - * - Uses a `UserRepo` as the environment - */ -object UserApp { - def apply(): Http[UserRepo, Nothing, Request, Response] = - Http.collectZIO[Request] { - // POST /users -d '{"name": "John", "age": 35}' - case req @ Method.POST -> Root / "users" => - (for { - u <- req.body.asString.map(_.fromJson[User]) - r <- u match { - case Left(e) => - ZIO - .debug(s"Failed to parse the input: $e") - .as( - Response.text(e).withStatus(Status.BadRequest) - ) - case Right(u) => - UserRepo - .register(u) - .map(id => Response.text(id)) - } - } yield r).orDie - - // GET /users/:id - case Method.GET -> Root / "users" / id => - UserRepo - .lookup(id) - .map { - case Some(user) => - Response.json(user.toJson) - case None => - Response.status(Status.NotFound) - } - .orDie - // GET /users - case Method.GET -> Root / "users" => - UserRepo.users.map(response => Response.json(response.toJson)).orDie - } - -} diff --git a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala new file mode 100644 index 0000000..3844dc3 --- /dev/null +++ b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala @@ -0,0 +1,54 @@ +package dev.zio.quickstart.users + +import zio._ +import zio.http._ +import zio.schema.codec.JsonCodec.schemaBasedBinaryCodec + +/** Collection of routes that: + * - Accept a `Request` and returns a `Response` + * - May fail with type of `Response` + * - Require a `UserRepo` from the environment + */ +object UserRoutes { + def apply(): Routes[UserRepo, Response] = + Routes( + // POST /users -d '{"name": "John", "age": 35}' + Method.POST / "users" -> handler { (req: Request) => + for { + u <- req.body.to[User].orElseFail(Response.badRequest) + r <- + UserRepo + .register(u) + .mapBoth( + _ => + Response + .internalServerError(s"Failed to register the user: $u"), + id => Response.text(id) + ) + } yield r + }, + + // GET /users/:id + Method.GET / "users" / string("id") -> handler { + (id: String, _: Request) => + UserRepo + .lookup(id) + .mapBoth( + _ => Response.internalServerError(s"Cannot retrieve user $id"), + { + case Some(user) => + Response(body = Body.from(user)) + case None => + Response.notFound(s"User $id not found!") + } + ) + }, + // GET /users + Method.GET / "users" -> handler { + UserRepo.users.mapBoth( + _ => Response.internalServerError("Cannot retrieve users!"), + users => Response(body = Body.from(users)) + ) + } + ) +} \ No newline at end of file From 5b2a0d7f160aa18d6318ede9f35673a143c66aba Mon Sep 17 00:00:00 2001 From: Milad Khajavi Date: Mon, 29 Apr 2024 08:54:39 +0330 Subject: [PATCH 3/6] update webservice-custom-logger. --- .../build.sbt | 2 +- .../build.sbt | 4 +- .../scala/dev/zio/quickstart/MainApp.scala | 12 +-- .../zio/quickstart/counter/CounterApp.scala | 34 ------- .../zio/quickstart/download/DownloadApp.scala | 45 --------- .../zio/quickstart/greet/GreetingApp.scala | 29 ------ .../dev/zio/quickstart/users/LogAspect.scala | 48 ++++----- .../scala/dev/zio/quickstart/users/User.scala | 7 +- .../dev/zio/quickstart/users/UserApp.scala | 99 ------------------- .../dev/zio/quickstart/users/UserRoutes.scala | 84 ++++++++++++++++ zio-quickstart-restful-webservice/build.sbt | 3 +- 11 files changed, 120 insertions(+), 247 deletions(-) delete mode 100644 zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala delete mode 100644 zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala delete mode 100644 zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala delete mode 100644 zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/UserApp.scala create mode 100644 zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala diff --git a/zio-quickstart-restful-webservice-configurable-app/build.sbt b/zio-quickstart-restful-webservice-configurable-app/build.sbt index f3dbaa0..5d596f8 100644 --- a/zio-quickstart-restful-webservice-configurable-app/build.sbt +++ b/zio-quickstart-restful-webservice-configurable-app/build.sbt @@ -12,4 +12,4 @@ libraryDependencies ++= Seq( "dev.zio" %% "zio-config-magnolia" % "4.0.0-RC16" ) -resolvers += Resolver.sonatypeOssRepos("snapshots") +resolvers ++= Resolver.sonatypeOssRepos("snapshots") diff --git a/zio-quickstart-restful-webservice-custom-logger/build.sbt b/zio-quickstart-restful-webservice-custom-logger/build.sbt index c72a06e..4699e77 100644 --- a/zio-quickstart-restful-webservice-custom-logger/build.sbt +++ b/zio-quickstart-restful-webservice-custom-logger/build.sbt @@ -3,7 +3,7 @@ scalaVersion := "2.13.8" libraryDependencies ++= Seq( "dev.zio" %% "zio" % "2.0.19", "dev.zio" %% "zio-json" % "0.6.2", - "dev.zio" %% "zio-http" % "3.0.0-RC2", + "dev.zio" %% "zio-http" % "3.0.0-RC6+36-d283e073-SNAPSHOT", "io.getquill" %% "quill-zio" % "4.7.0", "io.getquill" %% "quill-jdbc-zio" % "4.7.0", "com.h2database" % "h2" % "2.2.224", @@ -11,3 +11,5 @@ libraryDependencies ++= Seq( "dev.zio" %% "zio-logging-slf4j" % "2.1.15", "org.slf4j" % "slf4j-simple" % "2.0.9" ) + +resolvers ++= Resolver.sonatypeOssRepos("snapshots") diff --git a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/MainApp.scala b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/MainApp.scala index 006bf4a..2a62147 100644 --- a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/MainApp.scala +++ b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/MainApp.scala @@ -1,9 +1,6 @@ package dev.zio.quickstart -import dev.zio.quickstart.counter.CounterApp -import dev.zio.quickstart.download.DownloadApp -import dev.zio.quickstart.greet.GreetingApp -import dev.zio.quickstart.users.{InmemoryUserRepo, UserApp, UserRepo} +import dev.zio.quickstart.users.{InmemoryUserRepo, UserRoutes} import zio._ import zio.http._ import zio.logging.LogFormat @@ -11,14 +8,11 @@ import zio.logging.backend.SLF4J object MainApp extends ZIOAppDefault { override val bootstrap: ZLayer[Any, Nothing, Unit] = - SLF4J.slf4j(LogLevel.All, LogFormat.colored) + SLF4J.slf4j(LogFormat.colored) def run = { - val httpApps = GreetingApp() ++ DownloadApp() ++ CounterApp() ++ UserApp() Server - .serve( - httpApps.withDefaultErrorResponse - ) + .serve(UserRoutes()) .provide( Server.defaultWithPort(8080), diff --git a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala deleted file mode 100644 index f0aff1b..0000000 --- a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala +++ /dev/null @@ -1,34 +0,0 @@ -package dev.zio.quickstart.counter - -//import zhttp.http._ -import zio.http._ -import zio.{Ref, ZIO} - -/** An http app that: - * - Accepts `Request` and returns a `Response` - * - Does not fail - * - Requires the `Ref[Int]` as the environment - */ -object CounterApp { - def apply(): Http[Ref[Int], Throwable, Request, Response] = - Http.collectZIO[Request] { - case Method.GET -> Root / "up" => - ZIO.serviceWithZIO[Ref[Int]] { ref => - ref - .updateAndGet(_ + 1) - .map(_.toString) - .map(Response.text) - } - case Method.GET -> Root / "down" => - ZIO.serviceWithZIO[Ref[Int]] { ref => - ref - .updateAndGet(_ - 1) - .map(_.toString) - .map(Response.text) - } - case Method.GET -> Root / "get" => - ZIO.serviceWithZIO[Ref[Int]](ref => - ref.get.map(_.toString).map(Response.text) - ) - } -} diff --git a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala deleted file mode 100644 index 0299fef..0000000 --- a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala +++ /dev/null @@ -1,45 +0,0 @@ -package dev.zio.quickstart.download - -//import zhttp.http._ -import zio.http._ -import zio._ -import zio.stream.ZStream - -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - May fail with type of `Throwable` - * - Does not require any environment - */ -object DownloadApp { - def apply(): Http[Any, Throwable, Request, Response] = - Http.collect[Request] { - // GET /download - case Method.GET -> Root / "download" => - val fileName = "file.txt" - http.Response( - status = Status.Ok, - headers = Headers( - Header.ContentType(MediaType.application.`octet-stream`), - Header.ContentDisposition.attachment(fileName) - ), - body = Body.fromStream(ZStream.fromResource(fileName)) - ) - - // Download a large file using streams - // GET /download/stream - case Method.GET -> Root / "download" / "stream" => - val file = "bigfile.txt" - http.Response( - status = Status.Ok, - headers = Headers( - Header.ContentType(MediaType.application.`octet-stream`), - Header.ContentDisposition.attachment(file) - ), - body = Body.fromStream( - ZStream - .fromResource(file) - .schedule(Schedule.spaced(50.millis)) - ) - ) - } -} diff --git a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala deleted file mode 100644 index 44565d1..0000000 --- a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala +++ /dev/null @@ -1,29 +0,0 @@ -package dev.zio.quickstart.greet - -//import zhttp.http._ -import zio.http._ - -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - Does not fail - * - Does not use the environment - */ -object GreetingApp { - def apply(): Http[Any, Throwable, Request, Response] = - Http.collect[Request] { - // GET /greet?name=:name - case req @ (Method.GET -> Root / "greet") - if (req.url.queryParams.nonEmpty) => - Response.text( - s"Hello ${req.url.queryParams.get("name").map(_.mkString(" and "))}!" - ) - - // GET /greet - case Method.GET -> Root / "greet" => - Response.text(s"Hello World!") - - // GET /greet/:name - case Method.GET -> Root / "greet" / name => - Response.text(s"Hello $name!") - } -} diff --git a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/LogAspect.scala b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/LogAspect.scala index e9008f0..a56dea7 100644 --- a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/LogAspect.scala +++ b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/LogAspect.scala @@ -5,32 +5,32 @@ import zio.http._ import zio.prelude.data.Optional.AllValuesAreNullable object LogAspect { + def logAnnotateCorrelationId: Middleware[Any] = + new Middleware[Any] { + override def apply[Env1 <: Any, Err]( + app: Routes[Env1, Err] + ): Routes[Env1, Err] = + app.transform { h => + handler { (req: Request) => + def correlationId(req: Request): UIO[String] = + ZIO + .succeed(req.headers.get("X-Correlation-ID")) + .flatMap(x => + Random.nextUUID.map(uuid => x.getOrElse(uuid.toString)) + ) - def logSpan( - label: String - ): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] = - new ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] { - override def apply[R, E, A](zio: ZIO[R, E, A])(implicit - trace: Trace - ): ZIO[R, E, A] = - ZIO.logSpan(label)(zio) + correlationId(req).flatMap(id => + ZIO.logAnnotate("correlation-id", id)(h(req)) + ) + } + } } - def logAnnotateCorrelationId( - req: Request - ): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] = - new ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] { - override def apply[R, E, A]( - zio: ZIO[R, E, A] - )(implicit trace: Trace): ZIO[R, E, A] = - correlationId(req).flatMap(id => - ZIO.logAnnotate("correlation-id", id)(zio) - ) - - def correlationId(req: Request): UIO[String] = - ZIO - .succeed(req.headers.get("X-Correlation-ID")) - .map(_.toString) - .flatMap(x => Random.nextUUID.map(uuid => x.getOrElse(uuid.toString))) + def logSpan(label: String): HandlerAspect[Any, Unit] = + HandlerAspect.interceptIncomingHandler { + Handler.fromFunctionZIO { (req: Request) => + ZIO.logSpan(label)(ZIO.succeed(req).map(r => (r, ()))) + } } + } diff --git a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/User.scala b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/User.scala index 32c0a16..c758e5c 100644 --- a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/User.scala +++ b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/User.scala @@ -2,12 +2,11 @@ package dev.zio.quickstart.users import java.util.UUID import zio.json._ +import zio.schema.{DeriveSchema, Schema} case class User(name: String, age: Int) object User { - implicit val encoder: JsonEncoder[User] = - DeriveJsonEncoder.gen[User] - implicit val decoder: JsonDecoder[User] = - DeriveJsonDecoder.gen[User] + implicit val schema: Schema[User] = + DeriveSchema.gen[User] } diff --git a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/UserApp.scala b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/UserApp.scala deleted file mode 100644 index 1a4de69..0000000 --- a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/UserApp.scala +++ /dev/null @@ -1,99 +0,0 @@ -package dev.zio.quickstart.users - -import zio._ -import zio.http._ -import zio.json._ - -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - May fail with type of `Throwable` - * - Uses a `UserRepo` as the environment - */ -object UserApp { - import LogAspect._ - - def apply(): Http[UserRepo, Throwable, Request, Response] = - Http.collectZIO[Request] { - // POST /users -d '{"name": "John", "age": 35}' - case req @ (Method.POST -> Root / "users") => - { - for { - body <- req.body.asString - _ <- ZIO.logInfo(s"POST /users -d $body") - u = body.fromJson[User] - r <- u match { - case Left(e) => - ZIO - .logErrorCause(s"Failed to parse the input", Cause.fail(e)) - .as(Response.text(e).withStatus(Status.BadRequest)) - case Right(u) => - UserRepo - .register(u) - .foldCauseZIO( - failure => - ZIO - .logErrorCause( - s"Failed to register user", - Cause.fail(failure) - ) - .as(Response.status(Status.InternalServerError)), - success => - ZIO - .logInfo(s"User registered: $success") - .as(Response.text(success)) - ) - } - } yield r - } @@ logSpan("register-user") @@ logAnnotateCorrelationId(req) - - // GET /users/:id - case req @ (Method.GET -> Root / "users" / id) => - { - for { - _ <- ZIO.logInfo(s"Request: GET /users/$id") - r <- UserRepo - .lookup(id) - .some - .foldZIO( - { - case Some(error) => - ZIO - .logErrorCause( - s"Failed to lookup user.", - Cause.fail(error) - ) - .as(Response.status(Status.InternalServerError)) - case None => - ZIO - .log(s"Requested user with $id not found") - .as(Response.status(Status.NotFound)) - }, - success => - ZIO - .log(s"Retrieved the user") - .as(Response.json(success.toJson)) - ) - } yield r - } @@ logSpan("get-user") @@ logAnnotateCorrelationId(req) - - // GET /users - case req @ (Method.GET -> Root / "users") => - { - for { - _ <- ZIO.logInfo(s"Request: GET /users") - users <- UserRepo.users.foldCauseZIO( - failure => - ZIO - .logErrorCause(s"Failed to retrieve users.", failure) - .as(Response.status(Status.InternalServerError)), - success => - ZIO - .log( - s"Retrieved users successfully: response length=${success.length}" - ) - .as(Response.json(success.toJson)) - ) - } yield users - } @@ logSpan("get-users") @@ logAnnotateCorrelationId(req) - } -} diff --git a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala new file mode 100644 index 0000000..1098cf7 --- /dev/null +++ b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala @@ -0,0 +1,84 @@ +package dev.zio.quickstart.users + +import zio._ +import zio.http._ +import zio.schema.codec.JsonCodec.schemaBasedBinaryCodec + +/** Collection of routes that: + * - Accept a `Request` and returns a `Response` + * - May fail with type of `Response` + * - Require a `UserRepo` from the environment + */ +object UserRoutes { + import LogAspect._ + + def apply(): Routes[UserRepo, Response] = + Routes( + // POST /users -d '{"name": "John", "age": 35}' + Method.POST / "users" -> handler { (req: Request) => + for { + _ <- ZIO.logInfo(s"The POST /users endpoint called") + u <- req.body + .to[User] + .catchAll(e => + ZIO + .logError(s"Failed to parse the input $e") + *> ZIO.fail(Response.badRequest("Failed to parse the input!")) + ) + r <- + UserRepo + .register(u) + .foldZIO( + e => + ZIO.logError(s"Failed to register user $e") *> + ZIO.fail( + Response + .internalServerError(s"Failed to register the user: $u") + ), + id => ZIO.succeed(Response.text(id)) + ) + } yield r + } @@ logSpan("register-user"), + + // GET /users/:id + Method.GET / "users" / string("id") -> handler { + (id: String, _: Request) => + ZIO.logInfo(s"The GET /users/$id endpoint called!") *> + UserRepo + .lookup(id) + .foldZIO( + e => + ZIO + .logError(s"Failed to lookup user $e") *> + ZIO.fail( + Response.internalServerError(s"Cannot retrieve user $id") + ), + { + case Some(user) => + ZIO + .log(s"Retrieved the user") + .as(Response(body = Body.from(user))) + case None => + ZIO.log(s"Requested user with $id not found") *> + ZIO.fail(Response.notFound(s"User $id not found!")) + } + ) + } @@ logSpan("get-user"), + // GET /users + Method.GET / "users" -> handler { + UserRepo.users.foldZIO( + e => + ZIO + .logError(s"Failed to retrieve users. $e") *> + ZIO.fail(Response.internalServerError("Cannot retrieve users!")), + users => + ZIO + .log( + s"Retrieved users successfully: response length=${users.length}" + ) + .as(Response(body = Body.from(users))) + ) + } @@ logSpan("get-users") + ) @@ logAnnotateCorrelationId + +} diff --git a/zio-quickstart-restful-webservice/build.sbt b/zio-quickstart-restful-webservice/build.sbt index 9cc1171..9cae580 100644 --- a/zio-quickstart-restful-webservice/build.sbt +++ b/zio-quickstart-restful-webservice/build.sbt @@ -1,5 +1,4 @@ scalaVersion := "3.3.1" -resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots" libraryDependencies ++= Seq( "dev.zio" %% "zio" % "2.0.22", @@ -9,3 +8,5 @@ libraryDependencies ++= Seq( "io.getquill" %% "quill-jdbc-zio" % "4.7.0", "com.h2database" % "h2" % "2.2.224" ) + +resolvers ++= Resolver.sonatypeOssRepos("snapshots") From 48d1587be10b1be2ff0e56db7e7a80fbf9060fca Mon Sep 17 00:00:00 2001 From: Milad Khajavi Date: Mon, 29 Apr 2024 09:13:56 +0330 Subject: [PATCH 4/6] update webservice-dockerize. --- .../build.sbt | 4 +- .../scala/dev/zio/quickstart/MainApp.scala | 13 ++--- .../{CounterApp.scala => CounterRoutes.scala} | 25 +++++----- ...DownloadApp.scala => DownloadRoutes.scala} | 22 ++++----- .../zio/quickstart/greet/GreetingApp.scala | 28 ----------- .../zio/quickstart/greet/GreetingRoutes.scala | 25 ++++++++++ .../scala/dev/zio/quickstart/users/User.scala | 8 +--- .../dev/zio/quickstart/users/UserApp.scala | 48 ------------------- .../dev/zio/quickstart/users/UserRoutes.scala | 48 +++++++++++++++++++ 9 files changed, 106 insertions(+), 115 deletions(-) rename zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/counter/{CounterApp.scala => CounterRoutes.scala} (54%) rename zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/download/{DownloadApp.scala => DownloadRoutes.scala} (69%) delete mode 100644 zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala create mode 100644 zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala delete mode 100644 zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/users/UserApp.scala create mode 100644 zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala diff --git a/zio-quickstart-restful-webservice-dockerize/build.sbt b/zio-quickstart-restful-webservice-dockerize/build.sbt index ef177e7..ed2df20 100644 --- a/zio-quickstart-restful-webservice-dockerize/build.sbt +++ b/zio-quickstart-restful-webservice-dockerize/build.sbt @@ -3,7 +3,7 @@ scalaVersion := "2.13.8" libraryDependencies ++= Seq( "dev.zio" %% "zio" % "2.0.19", "dev.zio" %% "zio-json" % "0.6.2", - "dev.zio" %% "zio-http" % "3.0.0-RC2", + "dev.zio" %% "zio-http" % "3.0.0-RC6+36-d283e073-SNAPSHOT", "io.getquill" %% "quill-zio" % "4.7.0", "io.getquill" %% "quill-jdbc-zio" % "4.7.0", "com.h2database" % "h2" % "2.2.224" @@ -16,3 +16,5 @@ dockerExposedPorts := Seq(8080) dockerUsername := sys.props.get("docker.username") dockerRepository := sys.props.get("docker.registry") + +resolvers ++= Resolver.sonatypeOssRepos("snapshots") diff --git a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/MainApp.scala b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/MainApp.scala index f2ea93a..e79420d 100644 --- a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/MainApp.scala +++ b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/MainApp.scala @@ -1,17 +1,18 @@ package dev.zio.quickstart -import dev.zio.quickstart.counter.CounterApp -import dev.zio.quickstart.download.DownloadApp -import dev.zio.quickstart.greet.GreetingApp -import dev.zio.quickstart.users.{InmemoryUserRepo, PersistentUserRepo, UserApp} +import dev.zio.quickstart.counter.CounterRoutes +import dev.zio.quickstart.download.DownloadRoutes +import dev.zio.quickstart.greet.GreetingRoutes +import dev.zio.quickstart.users.{InmemoryUserRepo, UserRoutes} import zio._ import zio.http._ object MainApp extends ZIOAppDefault { def run = { - val httpApps = GreetingApp() ++ DownloadApp() ++ CounterApp() ++ UserApp() Server - .serve(httpApps.withDefaultErrorResponse) + .serve( + GreetingRoutes() ++ DownloadRoutes() ++ CounterRoutes() ++ UserRoutes() + ) .provide( Server.defaultWithPort(8080), diff --git a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/counter/CounterRoutes.scala similarity index 54% rename from zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala rename to zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/counter/CounterRoutes.scala index aa82a67..408b603 100644 --- a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala +++ b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/counter/CounterRoutes.scala @@ -1,33 +1,30 @@ package dev.zio.quickstart.counter - +import zio._ import zio.http._ -import zio.{Ref, ZIO} -/** An http app that: - * - Accepts `Request` and returns a `Response` - * - Does not fail - * - Requires the `Ref[Int]` as the environment - */ -object CounterApp { - def apply(): Http[Ref[Int], Nothing, Request, Response] = - Http.collectZIO[Request] { - case Method.GET -> Root / "up" => +object CounterRoutes { + def apply(): Routes[Ref[Int], Nothing] = + Routes( + Method.GET / "up" -> handler { ZIO.serviceWithZIO[Ref[Int]] { ref => ref .updateAndGet(_ + 1) .map(_.toString) .map(Response.text) } - case Method.GET -> Root / "down" => + }, + Method.GET / "down" -> handler { ZIO.serviceWithZIO[Ref[Int]] { ref => ref .updateAndGet(_ - 1) .map(_.toString) .map(Response.text) } - case Method.GET -> Root / "get" => + }, + Method.GET / "get" -> handler { ZIO.serviceWithZIO[Ref[Int]](ref => ref.get.map(_.toString).map(Response.text) ) - } + } + ) } diff --git a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/download/DownloadRoutes.scala similarity index 69% rename from zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala rename to zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/download/DownloadRoutes.scala index ec9969f..82b922a 100644 --- a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala +++ b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/download/DownloadRoutes.scala @@ -2,18 +2,14 @@ package dev.zio.quickstart.download import zio._ import zio.http._ -import zio.stream.ZStream +import zio.schema.codec.JsonCodec.schemaBasedBinaryCodec +import zio.stream._ -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - May fail with type of `Throwable` - * - Does not require any environment - */ -object DownloadApp { - def apply(): Http[Any, Throwable, Request, Response] = - Http.collect[Request] { +object DownloadRoutes { + def apply(): Routes[Any, Nothing] = + Routes( // GET /download - case Method.GET -> Root / "download" => + Method.GET / Root / "download" -> handler { val fileName = "file.txt" http.Response( status = Status.Ok, @@ -23,10 +19,11 @@ object DownloadApp { ), body = Body.fromStream(ZStream.fromResource(fileName)) ) + }, // Download a large file using streams // GET /download/stream - case Method.GET -> Root / "download" / "stream" => + Method.GET / "download" / "stream" -> handler { val file = "bigfile.txt" http.Response( status = Status.Ok, @@ -40,5 +37,6 @@ object DownloadApp { .schedule(Schedule.spaced(50.millis)) ) ) - } + } + ) } diff --git a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala deleted file mode 100644 index 2247925..0000000 --- a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala +++ /dev/null @@ -1,28 +0,0 @@ -package dev.zio.quickstart.greet - -import zio.http._ - -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - Does not fail - * - Does not use the environment - */ -object GreetingApp { - def apply(): Http[Any, Nothing, Request, Response] = - Http.collect[Request] { - // GET /greet?name=:name - case req @ (Method.GET -> Root / "greet") - if (req.url.queryParams.nonEmpty) => - Response.text( - s"Hello ${req.url.queryParams.get("name").map(_.mkString(" and "))}!" - ) - - // GET /greet - case Method.GET -> Root / "greet" => - Response.text(s"Hello World!") - - // GET /greet/:name - case Method.GET -> Root / "greet" / name => - Response.text(s"Hello $name!") - } -} diff --git a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala new file mode 100644 index 0000000..260739c --- /dev/null +++ b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala @@ -0,0 +1,25 @@ +package dev.zio.quickstart.greet +import zio.http._ + +object GreetingRoutes { + def apply(): Routes[Any, Nothing] = + Routes( + // GET /greet?name=:name + Method.GET / "greet" -> handler { (req: Request) => + if (req.url.queryParams.nonEmpty) + Response.text( + s"Hello ${req.url.queryParams("name").map(_.mkString(" and "))}!" + ) + else Response.badRequest("The name query parameter is missing!") + }, + + // GET /greet + Method.GET / "greet" -> handler(Response.text(s"Hello World!")), + + // GET /greet/:name + Method.GET / "greet" / string("name") -> handler { + (name: String, _: Request) => + Response.text(s"Hello $name!") + } + ) +} diff --git a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/users/User.scala b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/users/User.scala index 32c0a16..3bf2278 100644 --- a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/users/User.scala +++ b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/users/User.scala @@ -1,13 +1,9 @@ package dev.zio.quickstart.users -import java.util.UUID -import zio.json._ +import zio.schema._ case class User(name: String, age: Int) object User { - implicit val encoder: JsonEncoder[User] = - DeriveJsonEncoder.gen[User] - implicit val decoder: JsonDecoder[User] = - DeriveJsonDecoder.gen[User] + implicit val schmea: Schema[User] = DeriveSchema.gen[User] } diff --git a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/users/UserApp.scala b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/users/UserApp.scala deleted file mode 100644 index 3f3fa40..0000000 --- a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/users/UserApp.scala +++ /dev/null @@ -1,48 +0,0 @@ -package dev.zio.quickstart.users - -import zio._ -import zio.http._ -import zio.json._ - -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - May fail with type of `Throwable` - * - Uses a `UserRepo` as the environment - */ -object UserApp { - def apply(): Http[UserRepo, Throwable, Request, Response] = - Http.collectZIO[Request] { - // POST /users -d '{"name": "John", "age": 35}' - case req @ (Method.POST -> Root / "users") => - for { - u <- req.body.asString.map(_.fromJson[User]) - r <- u match { - case Left(e) => - ZIO - .debug(s"Failed to parse the input: $e") - .as( - Response.text(e).withStatus(Status.BadRequest) - ) - case Right(u) => - UserRepo - .register(u) - .map(id => Response.text(id)) - } - } yield r - - // GET /users/:id - case Method.GET -> Root / "users" / id => - UserRepo - .lookup(id) - .map { - case Some(user) => - Response.json(user.toJson) - case None => - Response.status(Status.NotFound) - } - // GET /users - case Method.GET -> Root / "users" => - UserRepo.users.map(response => Response.json(response.toJson)) - } - -} diff --git a/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala new file mode 100644 index 0000000..78d7706 --- /dev/null +++ b/zio-quickstart-restful-webservice-dockerize/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala @@ -0,0 +1,48 @@ +package dev.zio.quickstart.users + +import zio.http._ +import zio.schema.codec.JsonCodec.schemaBasedBinaryCodec + +object UserRoutes { + def apply(): Routes[UserRepo, Response] = + Routes( + // POST /users -d '{"name": "John", "age": 35}' + Method.POST / "users" -> handler { (req: Request) => + for { + u <- req.body.to[User].orElseFail(Response.badRequest) + r <- + UserRepo + .register(u) + .mapBoth( + _ => + Response + .internalServerError(s"Failed to register the user: $u"), + id => Response.text(id) + ) + } yield r + }, + + // GET /users/:id + Method.GET / "users" / string("id") -> handler { + (id: String, _: Request) => + UserRepo + .lookup(id) + .mapBoth( + _ => Response.internalServerError(s"Cannot retrieve user $id"), + { + case Some(user) => + Response(body = Body.from(user)) + case None => + Response.notFound(s"User $id not found!") + } + ) + }, + // GET /users + Method.GET / "users" -> handler { + UserRepo.users.mapBoth( + _ => Response.internalServerError("Cannot retrieve users!"), + users => Response(body = Body.from(users)) + ) + } + ) +} From 4107ef88e2f815958d6096acee95246ca075f9ef Mon Sep 17 00:00:00 2001 From: Milad Khajavi Date: Mon, 29 Apr 2024 10:06:18 +0330 Subject: [PATCH 5/6] update webservice-metrics. --- .../build.sbt | 4 +- .../scala/dev/zio/quickstart/MainApp.scala | 11 +-- .../zio/quickstart/counter/CounterApp.scala | 33 -------- .../zio/quickstart/download/DownloadApp.scala | 44 ---------- .../zio/quickstart/greet/GreetingApp.scala | 28 ------- .../prometheus/PrometheusPublisherApp.scala | 11 +-- .../scala/dev/zio/quickstart/users/User.scala | 6 +- .../dev/zio/quickstart/users/UserApp.scala | 52 ------------ .../dev/zio/quickstart/users/UserRoutes.scala | 84 +++++++++++++++++++ 9 files changed, 97 insertions(+), 176 deletions(-) delete mode 100644 zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala delete mode 100644 zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala delete mode 100644 zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala delete mode 100644 zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/UserApp.scala create mode 100644 zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala diff --git a/zio-quickstart-restful-webservice-metrics/build.sbt b/zio-quickstart-restful-webservice-metrics/build.sbt index cbcf5bd..5655353 100644 --- a/zio-quickstart-restful-webservice-metrics/build.sbt +++ b/zio-quickstart-restful-webservice-metrics/build.sbt @@ -6,8 +6,10 @@ libraryDependencies ++= Seq( "dev.zio" %% "zio" % "2.0.19", "dev.zio" %% "zio-metrics-connectors" % "2.0.8", "dev.zio" %% "zio-json" % "0.6.2", - "dev.zio" %% "zio-http" % "3.0.0-RC2", + "dev.zio" %% "zio-http" % "3.0.0-RC6+36-d283e073-SNAPSHOT", "io.getquill" %% "quill-zio" % "4.7.0", "io.getquill" %% "quill-jdbc-zio" % "4.7.0", "com.h2database" % "h2" % "2.2.224" ) + +resolvers ++= Resolver.sonatypeOssRepos("snapshots") diff --git a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/MainApp.scala b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/MainApp.scala index d4b700f..c5bb9a0 100644 --- a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/MainApp.scala +++ b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/MainApp.scala @@ -1,10 +1,7 @@ package dev.zio.quickstart -import dev.zio.quickstart.counter.CounterApp -import dev.zio.quickstart.download.DownloadApp -import dev.zio.quickstart.greet.GreetingApp import dev.zio.quickstart.prometheus.PrometheusPublisherApp -import dev.zio.quickstart.users.{InmemoryUserRepo, UserApp} +import dev.zio.quickstart.users._ import zio._ import zio.http._ import zio.metrics.connectors.{MetricsConfig, prometheus} @@ -13,12 +10,8 @@ object MainApp extends ZIOAppDefault { private val metricsConfig = ZLayer.succeed(MetricsConfig(5.seconds)) def run = { - val httpApps = - GreetingApp() ++ DownloadApp() ++ CounterApp() ++ UserApp() ++ PrometheusPublisherApp() Server - .serve( - httpApps.withDefaultErrorResponse - ) + .serve(UserRoutes() ++ PrometheusPublisherApp()) .provide( Server.defaultWithPort(8080), diff --git a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala deleted file mode 100644 index aa82a67..0000000 --- a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/counter/CounterApp.scala +++ /dev/null @@ -1,33 +0,0 @@ -package dev.zio.quickstart.counter - -import zio.http._ -import zio.{Ref, ZIO} - -/** An http app that: - * - Accepts `Request` and returns a `Response` - * - Does not fail - * - Requires the `Ref[Int]` as the environment - */ -object CounterApp { - def apply(): Http[Ref[Int], Nothing, Request, Response] = - Http.collectZIO[Request] { - case Method.GET -> Root / "up" => - ZIO.serviceWithZIO[Ref[Int]] { ref => - ref - .updateAndGet(_ + 1) - .map(_.toString) - .map(Response.text) - } - case Method.GET -> Root / "down" => - ZIO.serviceWithZIO[Ref[Int]] { ref => - ref - .updateAndGet(_ - 1) - .map(_.toString) - .map(Response.text) - } - case Method.GET -> Root / "get" => - ZIO.serviceWithZIO[Ref[Int]](ref => - ref.get.map(_.toString).map(Response.text) - ) - } -} diff --git a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala deleted file mode 100644 index ec9969f..0000000 --- a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/download/DownloadApp.scala +++ /dev/null @@ -1,44 +0,0 @@ -package dev.zio.quickstart.download - -import zio._ -import zio.http._ -import zio.stream.ZStream - -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - May fail with type of `Throwable` - * - Does not require any environment - */ -object DownloadApp { - def apply(): Http[Any, Throwable, Request, Response] = - Http.collect[Request] { - // GET /download - case Method.GET -> Root / "download" => - val fileName = "file.txt" - http.Response( - status = Status.Ok, - headers = Headers( - Header.ContentType(MediaType.application.`octet-stream`), - Header.ContentDisposition.attachment(fileName) - ), - body = Body.fromStream(ZStream.fromResource(fileName)) - ) - - // Download a large file using streams - // GET /download/stream - case Method.GET -> Root / "download" / "stream" => - val file = "bigfile.txt" - http.Response( - status = Status.Ok, - headers = Headers( - Header.ContentType(MediaType.application.`octet-stream`), - Header.ContentDisposition.attachment(file) - ), - body = Body.fromStream( - ZStream - .fromResource(file) - .schedule(Schedule.spaced(50.millis)) - ) - ) - } -} diff --git a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala deleted file mode 100644 index 8f2bbbe..0000000 --- a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/greet/GreetingApp.scala +++ /dev/null @@ -1,28 +0,0 @@ -package dev.zio.quickstart.greet - -import zio.http._ - -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - Does not fail - * - Does not use the environment - */ -object GreetingApp { - def apply(): Http[Any, Nothing, Request, Response] = - Http.collect[Request] { - // GET /greet?name=:name - case req @ (Method.GET -> Root / "greet") - if (req.url.queryParams.nonEmpty) => - Response.text( - s"Hello ${req.url.queryParams.get("name") map (_.mkString(" and "))}!" - ) - - // GET /greet - case Method.GET -> Root / "greet" => - Response.text(s"Hello World!") - - // GET /greet/:name - case Method.GET -> Root / "greet" / name => - Response.text(s"Hello $name!") - } -} diff --git a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/prometheus/PrometheusPublisherApp.scala b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/prometheus/PrometheusPublisherApp.scala index ffe7f5e..cd19cea 100644 --- a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/prometheus/PrometheusPublisherApp.scala +++ b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/prometheus/PrometheusPublisherApp.scala @@ -5,9 +5,10 @@ import zio.http._ import zio.metrics.connectors.prometheus.PrometheusPublisher object PrometheusPublisherApp { - def apply(): Http[PrometheusPublisher, Nothing, Request, Response] = { - Http.collectZIO[Request] { case Method.GET -> Root / "metrics" => - ZIO.serviceWithZIO[PrometheusPublisher](_.get.map(Response.text)) - } - } + def apply(): Routes[PrometheusPublisher, Nothing] = + Routes( + Method.GET / "metrics" -> handler( + ZIO.serviceWithZIO[PrometheusPublisher](_.get.map(Response.text)) + ) + ) } diff --git a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/User.scala b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/User.scala index 32c0a16..f5a93bc 100644 --- a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/User.scala +++ b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/User.scala @@ -2,12 +2,10 @@ package dev.zio.quickstart.users import java.util.UUID import zio.json._ +import zio.schema.{DeriveSchema, Schema} case class User(name: String, age: Int) object User { - implicit val encoder: JsonEncoder[User] = - DeriveJsonEncoder.gen[User] - implicit val decoder: JsonDecoder[User] = - DeriveJsonDecoder.gen[User] + implicit val schema: Schema[User] = DeriveSchema.gen[User] } diff --git a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/UserApp.scala b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/UserApp.scala deleted file mode 100644 index 05ab3ec..0000000 --- a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/UserApp.scala +++ /dev/null @@ -1,52 +0,0 @@ -package dev.zio.quickstart.users - -import zio._ -import zio.http._ -import zio.json._ -import dev.zio.quickstart._ - -/** An http app that: - * - Accepts a `Request` and returns a `Response` - * - May fail with type of `Throwable` - * - Uses a `UserRepo` as the environment - */ -object UserApp { - def apply(): Http[UserRepo, Throwable, Request, Response] = - Http.collectZIO[Request] { - // POST /users -d '{"name": "John", "age": 35}' - case req @ (Method.POST -> Root / "users") => - (for { - u <- req.body.asString.map(_.fromJson[User]) - r <- u match { - case Left(e) => - ZIO - .debug(s"Failed to parse the input: $e") - .as( - Response.text(e).withStatus(Status.BadRequest) - ) - case Right(u) => - UserRepo - .register(u) - .map(id => Response.text(id)) - } - } yield r) @@ countAllRequests("post", "/users") - - // GET /users/:id - case Method.GET -> Root / "users" / id => - UserRepo - .lookup(id) - .map { - case Some(user) => - Response.json(user.toJson) - case None => - Response.status(Status.NotFound) - } @@ countAllRequests("get", "/users/:id") - - // GET /users - case Method.GET -> Root / "users" => - UserRepo.users - .map(response => Response.json(response.toJson)) @@ - countAllRequests("get", "/users") - } - -} diff --git a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala new file mode 100644 index 0000000..50dce13 --- /dev/null +++ b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala @@ -0,0 +1,84 @@ +package dev.zio.quickstart.users + +import zio._ +import zio.http._ +import zio.schema.codec.JsonCodec.schemaBasedBinaryCodec + + +/** Collection of routes that: + * - Accept a `Request` and returns a `Response` + * - May fail with type of `Response` + * - Require a `UserRepo` from the environment + */ +object UserRoutes { + + def apply(): Routes[UserRepo, Response] = + Routes( + // POST /users -d '{"name": "John", "age": 35}' + Method.POST / "users" -> handler { (req: Request) => + for { + _ <- ZIO.logInfo(s"The POST /users endpoint called") + u <- req.body + .to[User] + .catchAll(e => + ZIO + .logError(s"Failed to parse the input $e") + *> ZIO.fail(Response.badRequest("Failed to parse the input!")) + ) + r <- + UserRepo + .register(u) + .foldZIO( + e => + ZIO.logError(s"Failed to register user $e") *> + ZIO.fail( + Response + .internalServerError(s"Failed to register the user: $u") + ), + id => ZIO.succeed(Response.text(id)) + ) + } yield r + }, + + // GET /users/:id + Method.GET / "users" / string("id") -> handler { + (id: String, _: Request) => + ZIO.logInfo(s"The GET /users/$id endpoint called!") *> + UserRepo + .lookup(id) + .foldZIO( + e => + ZIO + .logError(s"Failed to lookup user $e") *> + ZIO.fail( + Response.internalServerError(s"Cannot retrieve user $id") + ), + { + case Some(user) => + ZIO + .log(s"Retrieved the user") + .as(Response(body = Body.from(user))) + case None => + ZIO.log(s"Requested user with $id not found") *> + ZIO.fail(Response.notFound(s"User $id not found!")) + } + ) + }, + // GET /users + Method.GET / "users" -> handler { + UserRepo.users.foldZIO( + e => + ZIO + .logError(s"Failed to retrieve users. $e") *> + ZIO.fail(Response.internalServerError("Cannot retrieve users!")), + users => + ZIO + .log( + s"Retrieved users successfully: response length=${users.length}" + ) + .as(Response(body = Body.from(users))) + ) + }, + ) @@ Middleware.metrics() + +} From fa598493bbb2ef50af51a4bc32b21ca0d96c4e3e Mon Sep 17 00:00:00 2001 From: Milad Khajavi Date: Mon, 29 Apr 2024 10:10:07 +0330 Subject: [PATCH 6/6] fmt. --- .../main/scala/dev/zio/quickstart/MainApp.scala | 4 +++- .../zio/quickstart/counter/CounterRoutes.scala | 2 +- .../zio/quickstart/download/DownloadRoutes.scala | 2 +- .../dev/zio/quickstart/greet/GreetingRoutes.scala | 15 ++++++++------- .../dev/zio/quickstart/users/UserRoutes.scala | 4 ++-- .../scala/dev/zio/quickstart/users/User.scala | 2 +- .../dev/zio/quickstart/users/UserRoutes.scala | 6 +++--- .../build.sbt | 14 +++++++------- .../dev/zio/quickstart/users/UserRoutes.scala | 15 +++++++-------- .../main/scala/dev/zio/quickstart/MainApp.scala | 10 ++++++++-- .../dev/zio/quickstart/greet/GreetingRoutes.scala | 13 +++++++------ .../scala/dev/zio/quickstart/users/User.scala | 2 +- .../dev/zio/quickstart/users/UserRoutes.scala | 4 ++-- 13 files changed, 51 insertions(+), 42 deletions(-) diff --git a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/MainApp.scala b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/MainApp.scala index 99315e8..524bbc8 100644 --- a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/MainApp.scala +++ b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/MainApp.scala @@ -35,7 +35,9 @@ object MainApp extends ZIOAppDefault { def run = { (Server - .install(GreetingRoutes() ++ DownloadRoutes() ++ CounterRoutes() ++ UserRoutes()) + .install( + GreetingRoutes() ++ DownloadRoutes() ++ CounterRoutes() ++ UserRoutes() + ) .flatMap(port => Console.printLine(s"Started server on port: $port") ) *> ZIO.never) diff --git a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/counter/CounterRoutes.scala b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/counter/CounterRoutes.scala index 8d0ea70..84ab0f0 100644 --- a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/counter/CounterRoutes.scala +++ b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/counter/CounterRoutes.scala @@ -33,4 +33,4 @@ object CounterRoutes { ) } ) -} \ No newline at end of file +} diff --git a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/download/DownloadRoutes.scala b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/download/DownloadRoutes.scala index df5a5b2..88108fa 100644 --- a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/download/DownloadRoutes.scala +++ b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/download/DownloadRoutes.scala @@ -43,4 +43,4 @@ object DownloadRoutes { ) } ) -} \ No newline at end of file +} diff --git a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala index 5f0b5e3..95fa9c6 100644 --- a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala +++ b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala @@ -3,10 +3,10 @@ package dev.zio.quickstart.greet import zio.http._ /** Collection of routes that: - * - Accept a `Request` and return a `Response` - * - Do not fail - * - Do not use the environment - */ + * - Accept a `Request` and return a `Response` + * - Do not fail + * - Do not use the environment + */ object GreetingRoutes { def apply(): Routes[Any, Nothing] = Routes( @@ -23,8 +23,9 @@ object GreetingRoutes { Method.GET / "greet" -> handler(Response.text(s"Hello World!")), // GET /greet/:name - Method.GET / "greet" / string("name") -> handler { (name: String, _: Request) => - Response.text(s"Hello $name!") + Method.GET / "greet" / string("name") -> handler { + (name: String, _: Request) => + Response.text(s"Hello $name!") } ) -} \ No newline at end of file +} diff --git a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala index 3844dc3..91a2378 100644 --- a/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala +++ b/zio-quickstart-restful-webservice-configurable-app/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala @@ -2,7 +2,7 @@ package dev.zio.quickstart.users import zio._ import zio.http._ -import zio.schema.codec.JsonCodec.schemaBasedBinaryCodec +import zio.schema.codec.JsonCodec.schemaBasedBinaryCodec /** Collection of routes that: * - Accept a `Request` and returns a `Response` @@ -51,4 +51,4 @@ object UserRoutes { ) } ) -} \ No newline at end of file +} diff --git a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/User.scala b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/User.scala index c758e5c..3d8d914 100644 --- a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/User.scala +++ b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/User.scala @@ -7,6 +7,6 @@ import zio.schema.{DeriveSchema, Schema} case class User(name: String, age: Int) object User { - implicit val schema: Schema[User] = + implicit val schema: Schema[User] = DeriveSchema.gen[User] } diff --git a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala index 1098cf7..d0236f8 100644 --- a/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala +++ b/zio-quickstart-restful-webservice-custom-logger/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala @@ -11,7 +11,7 @@ import zio.schema.codec.JsonCodec.schemaBasedBinaryCodec */ object UserRoutes { import LogAspect._ - + def apply(): Routes[UserRepo, Response] = Routes( // POST /users -d '{"name": "John", "age": 35}' @@ -63,7 +63,7 @@ object UserRoutes { ZIO.fail(Response.notFound(s"User $id not found!")) } ) - } @@ logSpan("get-user"), + } @@ logSpan("get-user"), // GET /users Method.GET / "users" -> handler { UserRepo.users.foldZIO( @@ -78,7 +78,7 @@ object UserRoutes { ) .as(Response(body = Body.from(users))) ) - } @@ logSpan("get-users") + } @@ logSpan("get-users") ) @@ logAnnotateCorrelationId } diff --git a/zio-quickstart-restful-webservice-metrics/build.sbt b/zio-quickstart-restful-webservice-metrics/build.sbt index 5655353..decc9f3 100644 --- a/zio-quickstart-restful-webservice-metrics/build.sbt +++ b/zio-quickstart-restful-webservice-metrics/build.sbt @@ -3,13 +3,13 @@ organization := "dev.zio" name := "zio-quickstart-restful-webservice" libraryDependencies ++= Seq( - "dev.zio" %% "zio" % "2.0.19", - "dev.zio" %% "zio-metrics-connectors" % "2.0.8", - "dev.zio" %% "zio-json" % "0.6.2", - "dev.zio" %% "zio-http" % "3.0.0-RC6+36-d283e073-SNAPSHOT", - "io.getquill" %% "quill-zio" % "4.7.0", - "io.getquill" %% "quill-jdbc-zio" % "4.7.0", - "com.h2database" % "h2" % "2.2.224" + "dev.zio" %% "zio" % "2.0.19", + "dev.zio" %% "zio-metrics-connectors" % "2.0.8", + "dev.zio" %% "zio-json" % "0.6.2", + "dev.zio" %% "zio-http" % "3.0.0-RC6+36-d283e073-SNAPSHOT", + "io.getquill" %% "quill-zio" % "4.7.0", + "io.getquill" %% "quill-jdbc-zio" % "4.7.0", + "com.h2database" % "h2" % "2.2.224" ) resolvers ++= Resolver.sonatypeOssRepos("snapshots") diff --git a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala index 50dce13..63ae140 100644 --- a/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala +++ b/zio-quickstart-restful-webservice-metrics/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala @@ -4,12 +4,11 @@ import zio._ import zio.http._ import zio.schema.codec.JsonCodec.schemaBasedBinaryCodec - /** Collection of routes that: - * - Accept a `Request` and returns a `Response` - * - May fail with type of `Response` - * - Require a `UserRepo` from the environment - */ + * - Accept a `Request` and returns a `Response` + * - May fail with type of `Response` + * - Require a `UserRepo` from the environment + */ object UserRoutes { def apply(): Routes[UserRepo, Response] = @@ -38,7 +37,7 @@ object UserRoutes { id => ZIO.succeed(Response.text(id)) ) } yield r - }, + }, // GET /users/:id Method.GET / "users" / string("id") -> handler { @@ -78,7 +77,7 @@ object UserRoutes { ) .as(Response(body = Body.from(users))) ) - }, - ) @@ Middleware.metrics() + } + ) @@ Middleware.metrics() } diff --git a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/MainApp.scala b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/MainApp.scala index 8d8f0c1..8f26258 100644 --- a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/MainApp.scala +++ b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/MainApp.scala @@ -3,14 +3,20 @@ package dev.zio.quickstart import dev.zio.quickstart.counter.CounterRoutes import dev.zio.quickstart.download.DownloadRoutes import dev.zio.quickstart.greet.GreetingRoutes -import dev.zio.quickstart.users.{InmemoryUserRepo, PersistentUserRepo, UserRoutes} +import dev.zio.quickstart.users.{ + InmemoryUserRepo, + PersistentUserRepo, + UserRoutes +} import zio._ import zio.http._ object MainApp extends ZIOAppDefault: def run = Server - .serve(GreetingRoutes() ++ DownloadRoutes() ++ CounterRoutes() ++ UserRoutes()) + .serve( + GreetingRoutes() ++ DownloadRoutes() ++ CounterRoutes() ++ UserRoutes() + ) .provide( Server.defaultWithPort(8080), diff --git a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala index c6c1124..7bb82fa 100644 --- a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala +++ b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/greet/GreetingRoutes.scala @@ -3,10 +3,10 @@ package dev.zio.quickstart.greet import zio.http.* /** Collection of routes that: - * - Accept a `Request` and return a `Response` - * - Do not fail - * - Do not use the environment - */ + * - Accept a `Request` and return a `Response` + * - Do not fail + * - Do not use the environment + */ object GreetingRoutes: def apply(): Routes[Any, Nothing] = Routes( @@ -23,7 +23,8 @@ object GreetingRoutes: Method.GET / "greet" -> handler(Response.text(s"Hello World!")), // GET /greet/:name - Method.GET / "greet" / string("name") -> handler {( name: String, _: Request) => - Response.text(s"Hello $name!") + Method.GET / "greet" / string("name") -> handler { + (name: String, _: Request) => + Response.text(s"Hello $name!") } ) diff --git a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/User.scala b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/User.scala index 0091629..cd5a353 100644 --- a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/User.scala +++ b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/User.scala @@ -8,4 +8,4 @@ import zio.schema.DeriveSchema._ case class User(name: String, age: Int) object User: - given Schema[User] = DeriveSchema.gen[User] \ No newline at end of file + given Schema[User] = DeriveSchema.gen[User] diff --git a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala index cfce522..f1dc721 100644 --- a/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala +++ b/zio-quickstart-restful-webservice/src/main/scala/dev/zio/quickstart/users/UserRoutes.scala @@ -2,7 +2,7 @@ package dev.zio.quickstart.users import zio.* import zio.http.* -import zio.schema.codec.JsonCodec.schemaBasedBinaryCodec +import zio.schema.codec.JsonCodec.schemaBasedBinaryCodec /** Collection of routes that: * - Accept a `Request` and returns a `Response` @@ -47,7 +47,7 @@ object UserRoutes: Method.GET / "users" -> handler { UserRepo.users.mapBoth( _ => Response.internalServerError("Cannot retrieve users!"), - users => Response(body =Body.from(users)) + users => Response(body = Body.from(users)) ) } )