Smithy4s-ZIO

Introduction

Credits

Compliance Tests - wip

Published Modules

Notes

Usage

This library is currently available for Scala binary versions 2.13 and 3.1.

To use the latest version, include the following in your build.sbt:

libraryDependencies ++= Seq(
  "io.github.yisraelu" %% "smithy4s-zio-http" % "0.0.2"
)

The snapshot version is available via the Sonatype snapshots repository: 0.0.2-16-c6f264b-SNAPSHOT.

Http Server and Client Quickstart (borrows from Smithy4s example)

Below is a quick example of smithy4s in action. This page does not provide much explanation or detail. For more information on various aspects of smithy4s, read through the other sections of this documentation site.

This section will get you started with a simple sbt module that enables smithy4s code generation.

project/plugins.sbt

Add the smithy4s-sbt-codegen plugin to your build.

addSbtPlugin("com.disneystreaming.smithy4s" % "smithy4s-sbt-codegen" % "<version>")

build.sbt

Enable the plugin in your project, add the smithy and zio-http dependencies.

import smithy4s.codegen.Smithy4sCodegenPlugin

val example = project
  .in(file("modules/example"))
  .enablePlugins(Smithy4sCodegenPlugin)
  .settings(
    libraryDependencies ++= Seq(
      "io.github.yisraelu" %% "smithy4s-zio-http" % "0.0.2"
    )
  )

Smithy content

Now is the time to add some Smithy shapes to see what code generation can do for you. Following the setup above, the location for the Smithy content will change depending on what build tool you used.

Now let's define an API in Smithy. Create the following file:

And add the content below:

namespace smithy4s.hello

use alloy#simpleRestJson

@simpleRestJson
service HelloWorldService {
  version: "1.0.0",
  operations: [Hello]
}

@http(method: "POST", uri: "/{name}", code: 200)
operation Hello {
  input: Person,
  output: Greeting
}

structure Person {
  @httpLabel
  @required
  name: String,

  @httpQuery("town")
  town: String
}

structure Greeting {
  @required
  message: String
}

The Scala code corresponding to this smithy file will be generated the next time you compile your project.

Using the generated code

Now, let's use the generated code by the service. You need to create a scala file at the following location:

Implement your service by extending the generated Service trait. Wire up routes into server.

import example.hello._
import zio.{Task, ZIO,ZIOAppDefault, ExitCode, URIO}
import zio.http._
import com.comcast.ip4s._
import smithy4s.zio.http.SimpleRestJsonBuilder

object HelloWorldImpl extends HelloWorldService[Task] {
  def hello(name: String, town: Option[String]): Task[Greeting] = ZIO.succeed {
    town match {
      case None => Greeting(s"Hello $name!")
      case Some(t) => Greeting(s"Hello $name from $t!")
    }
  }
}

object Main extends ZIOAppDefault {

  val port =  Port.fromInt(9000).get
  val app: ZIO[Any, Throwable, HttpApp[Any]] = {
    for {
      _ <- zio.Console.printLine(s"Starting server on http://localhost:$port")
      routes <- SimpleRestJsonBuilder
        .routes(HelloWorldImpl)
        .lift
      app = routes.sandbox.toHttpApp
    } yield app
  }

  override def run: URIO[Any, ExitCode] = {
    app
      .flatMap(Server.serve(_).provide(Server.defaultWithPort(port.value)))
      .exitCode
  }
}

Run Service

Client Example

You can also generate a client using smithy4s.

import example.hello._
import smithy4s.zio.http._
import zio.http.{Client, URL}
import zio.{Scope, ZIO, ZIOAppArgs, ZIOAppDefault}

object ClientImpl extends ZIOAppDefault {

  private val helloWorldClient: ZIO[Client, Throwable, HelloWorldService[ResourcefulTask]] = {
    for {
      url <- ZIO.fromEither(URL.decode("http://localhost:9000"))
      client <- ZIO.service[Client]
      helloClient <- SimpleRestJsonBuilder(HelloWorldService)
        .client(client)
        .url(url)
        .lift
    } yield helloClient
  }

  val program = helloWorldClient.flatMap(c =>
    c.hello("Sam", Some("New York City"))
      .flatMap(greeting => zio.Console.printLine(greeting.message))
  )

  override def run: ZIO[Any & ZIOAppArgs & Scope, Any, Any] =
    program.exitCode.provide(Client.default, Scope.default)

}