From cc5b30e6eaa42ed2b598d42f42dd6df25c937733 Mon Sep 17 00:00:00 2001 From: Milad Khajavi Date: Sun, 28 Apr 2024 19:42:39 +0330 Subject: [PATCH] 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)) + ) + } + )