Skip to content

Commit

Permalink
Merge branch 'main' into fix-doc-gen-tags
Browse files Browse the repository at this point in the history
  • Loading branch information
987Nabil authored Sep 27, 2024
2 parents dfbda9d + ad6dac9 commit 53ec126
Show file tree
Hide file tree
Showing 34 changed files with 1,234 additions and 64 deletions.
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ val _ = sys.props += ("ZIOHttpLogLevel" -> Debug.ZIOHttpLogLevel)
ThisBuild / githubWorkflowEnv += ("JDK_JAVA_OPTIONS" -> "-Xms4G -Xmx8G -XX:+UseG1GC -Xss10M -XX:ReservedCodeCacheSize=1G -XX:NonProfiledCodeHeapSize=512m -Dfile.encoding=UTF-8")
ThisBuild / githubWorkflowEnv += ("SBT_OPTS" -> "-Xms4G -Xmx8G -XX:+UseG1GC -Xss10M -XX:ReservedCodeCacheSize=1G -XX:NonProfiledCodeHeapSize=512m -Dfile.encoding=UTF-8")

ThisBuild / resolvers ++= Resolver.sonatypeOssRepos("snapshots")

ThisBuild / githubWorkflowJavaVersions := Seq(
JavaSpec.graalvm(Graalvm.Distribution("graalvm"), "17"),
JavaSpec.graalvm(Graalvm.Distribution("graalvm"), "21"),
Expand Down
11 changes: 11 additions & 0 deletions docs/reference/handler.md
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,17 @@ In this example, the type of the handler before applying the `sandbox` operator

Without the `sandbox` operator, the compiler would complain about the unhandled `Throwable` error.

By default, sandboxed errors will result in a `500 Internal Server Error` response without a body. If you want to have all information about the error in the response body you can use a different (`ErrorResponseConfig`)[response/response.md#failure-responses-with-details] like `ErrorResponseConfig.debug`:

```scala mdoc:compile-only
import zio.http._
import java.nio.file._

Routes(
Method.GET / "file" ->
Handler.fromFile(Paths.get("file.txt").toFile).sandbox,
) @@ ErrorResponseConfig.debug
```
### Converting a `Handler` to an `Routes`

The `Handler#toRoutes` operator, converts a handler to an `Routes` to be served by the `Server`. The following example, shows an HTTP application that serves a simple "Hello, World!" response for all types of incoming requests:
Expand Down
50 changes: 50 additions & 0 deletions docs/reference/response/response.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,56 @@ val failedHandler = Handler.fail(new IOException())
failedHandler.mapErrorCause(Response.fromCause)
```

#### Failure Responses with Details

By default, the `Response.fromThrowable` and `Response.fromCause` methods create a response with a status code only. If we want to include additional details in the response, we have to hand over a `ErrorResponseConfig`.

```scala
/**
* Configuration for the response generation
*
* @param withErrorBody
* if true, includes the error message in the response body
* @param withStackTrace
* if true, includes the stack trace in the response body
* @param maxStackTraceDepth
* maximum number of stack trace lines to include in the response body. Set to
* 0 to include all lines.
* @param errorFormat
* the preferred format for the error response.
* If the context in which the response is created has access to an Accept header,
* the header will be used preferably to determine the format.
*/
final case class ErrorResponseConfig(
withErrorBody: Boolean = false,
withStackTrace: Boolean = false,
maxStackTraceDepth: Int = 10,
errorFormat: ErrorResponseConfig.ErrorFormat = ErrorResponseConfig.ErrorFormat.Html,
)
```

This config can not only be used directly, but can also configure how ZIO-HTTP internally converts a `Cause` or `Throwable` to a `Response`.
You can configure error responses globally by providing a custom `ErrorResponseConfig` via layer for example in the bootstrap of your application.
Or you can apply the config locally to some routes via middleware.

```scala mdoc
import zio.http._

object MyHttpApp extends ZIOAppDefault {
// Provide a custom ErrorResponseConfig via layer
// Equivalent to: val bootstrap = ErrorResponseConfig.configLayer(ErrorResponseConfig.debugConfig)
override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = ErrorResponseConfig.debugLayer

// Apply the ErrorResponseConfig.debug middleware to routes
// Equivalent to: val myRoutes = Handler.ok.toRoutes @@ ErrorResponseConfig.withConfig(ErrorResponseConfig.debugConfig)
val myRoutes = Handler.ok.toRoutes @@ ErrorResponseConfig.debug

override def run = ???
}
```

The debug config will include the error message and full stack trace in the response body.

:::note
In many cases, it is more convenient to use the `sandbox` method to automatically convert all failures into a corresponding `Response`. But in some cases, to have more granular control over the error handling, we may want to use `Response.fromCause` and `Response.fromThrowable` directly.
:::
Expand Down
2 changes: 1 addition & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ object Dependencies {
val ZioCliVersion = "0.5.0"
val ZioJsonVersion = "0.7.1"
val ZioParserVersion = "0.1.10"
val ZioSchemaVersion = "1.4.1"
val ZioSchemaVersion = "1.5.0"
val SttpVersion = "3.3.18"
val ZioConfigVersion = "4.0.2"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ final case class EndpointGen(config: Config) {
caseClasses = code.caseClasses,
enums = code.enums,
)
Nil -> s"$method.${Inline.RequestBodyType}"
code.imports -> s"$method.${Inline.RequestBodyType}"
}
case OpenAPI.ReferenceOr.Reference(SchemaRef(ref), _, _) => Nil -> ref
case other => throw new Exception(s"Unexpected request body schema: $other")
Expand All @@ -460,7 +460,7 @@ final case class EndpointGen(config: Config) {

val (outImports: Iterable[List[Code.Import]], outCodes: Iterable[Code.OutCode]) =
// TODO: ignore default for now. Not sure how to handle it
op.responses.collect {
op.responses.toSeq.collect {
case (OpenAPI.StatusOrDefault.StatusValue(status), OpenAPI.ReferenceOr.Reference(ResponseRef(key), _, _)) =>
val response = resolveResponseRef(openAPI, key)
val (imports, code) =
Expand Down
9 changes: 9 additions & 0 deletions zio-http-gen/src/test/resources/ComponentAliasKey.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package test.component

import zio.prelude.Newtype
import zio.schema.Schema
import java.util.UUID

object Key extends Newtype[UUID] {
implicit val schema: Schema[Key.Type] = Schema.primitive[UUID].transform(wrap, unwrap)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package test.api.v1

import test.component._
import zio.schema._

object Users {
import zio.http._
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package test.api.v1

import test.component._
import zio.schema._

object Users {
import zio.http._
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package test.api.v1

import test.component._
import zio.schema._

object Keywords {
import zio.http._
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
info:
title: Dummy Service
version: 0.0.1
servers:
- url: http://127.0.0.1:5000/
tags:
- name: Dummy_API
paths:
/api/text_by_key:
get:
operationId: text_by_key
description: Get a dictionary mapping keys to text
responses:
"200":
content:
application/json:
schema:
$ref: '#/components/schemas/ObjectWithDictionary'
description: OK
openapi: 3.0.3
components:
schemas:
Key:
type: string
format: uuid
ObjectWithDictionary:
type: object
required:
- dict
properties:
dict:
type: object
additionalProperties:
type: string
x-string-key-schema:
$ref: '#/components/schemas/Key'
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,7 @@ object EndpointGenSpec extends ZIOSpecDefault {
val expected = Code.File(
List("api", "v1", "Users.scala"),
pkgPath = List("api", "v1"),
imports = List(Code.Import.FromBase(path = "component._")),
imports = List(Code.Import.FromBase(path = "component._"), Code.Import.Absolute("zio.schema._")),
objects = List(
Code.Object(
"Users",
Expand Down Expand Up @@ -1026,7 +1026,7 @@ object EndpointGenSpec extends ZIOSpecDefault {
val expected = Code.File(
List("api", "v1", "Users.scala"),
pkgPath = List("api", "v1"),
imports = List(Code.Import.FromBase(path = "component._")),
imports = List(Code.Import.FromBase(path = "component._"), Code.Import.Absolute("zio.schema._")),
objects = List(
Code.Object(
"Users",
Expand Down
Loading

0 comments on commit 53ec126

Please sign in to comment.