Skip to content

Commit

Permalink
Use empty body for Unit payload in Endpoint (zio#3258)
Browse files Browse the repository at this point in the history
  • Loading branch information
987Nabil committed Jan 15, 2025
1 parent 6d9dce1 commit c948d89
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ object RoundtripSpec extends ZIOHttpSpec {
Post(20, "title", "body", 10),
)
},
test("simple get without payload") {
val healthCheckAPI = Endpoint(GET / "health-check").out[Unit]
val healthCheckHandler = healthCheckAPI.implementAs(())
testEndpoint(healthCheckAPI, Routes(healthCheckHandler), (), ())
},
test("simple get with query params from case class") {
val endpoint = Endpoint(GET / "query")
.query(HttpCodec.queryAll[Params])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ sealed trait HttpContentCodec[A] { self =>
}
}

private[http] val isUnitCodec: Boolean = choices.values.forall(_.schema == Schema[Unit])

def only(mediaType: MediaType): HttpContentCodec[A] =
if (lookup(mediaType).isEmpty) {
throw new IllegalArgumentException(s"MediaType $mediaType is not supported by $self")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,10 @@ private[http] object BodyCodec {
.lookup(field.contentType)
.toRight(HttpCodecError.CustomError("UnsupportedMediaType", s"MediaType: ${field.contentType}"))
codec0 match {
case Left(error) => ZIO.fail(error)
case Left(error) =>
Exit.fail(error)
case Right((_, BinaryCodecWithSchema(_, schema))) if schema == Schema[Unit] =>
ZIO.unit.asInstanceOf[IO[Throwable, A]]
Exit.unit.asInstanceOf[IO[Throwable, A]]
case Right((_, bc @ BinaryCodecWithSchema(_, schema))) =>
field.asChunk.flatMap { chunk => ZIO.fromEither(bc.codec(config).decode(chunk)) }.flatMap(validateZIO(schema))
}
Expand All @@ -128,9 +129,11 @@ private[http] object BodyCodec {
override def decodeFromBody(body: Body, config: CodecConfig)(implicit trace: Trace): IO[Throwable, A] = {
val codec0 = codecForBody(codec, body)
codec0 match {
case Left(error) => ZIO.fail(error)
case Left(error) =>
Exit.fail(error)
case Right((_, BinaryCodecWithSchema(_, schema))) if schema == Schema[Unit] =>
ZIO.unit.asInstanceOf[IO[Throwable, A]]
if (body.isEmpty) Exit.unit.asInstanceOf[IO[Throwable, A]]
else ZIO.fail(HttpCodecError.CustomError("InvalidBody", "Non-empty body cannot be decoded as Unit"))
case Right((_, bc @ BinaryCodecWithSchema(_, schema))) =>
body.asChunk.flatMap { chunk => ZIO.fromEither(bc.codec(config).decode(chunk)) }.flatMap(validateZIO(schema))
}
Expand All @@ -139,7 +142,9 @@ private[http] object BodyCodec {
def encodeToField(value: A, mediaTypes: Chunk[MediaTypeWithQFactor], name: String, config: CodecConfig)(implicit
trace: Trace,
): FormField = {
val (mediaType, bc @ BinaryCodecWithSchema(_, _)) = codec.chooseFirstOrDefault(mediaTypes)
val selected = codec.chooseFirstOrDefault(mediaTypes)
val mediaType = selected._1
val bc = selected._2
if (mediaType.binary) {
FormField.binaryField(
name,
Expand All @@ -158,8 +163,11 @@ private[http] object BodyCodec {
def encodeToBody(value: A, mediaTypes: Chunk[MediaTypeWithQFactor], config: CodecConfig)(implicit
trace: Trace,
): Body = {
val (mediaType, bc @ BinaryCodecWithSchema(_, _)) = codec.chooseFirstOrDefault(mediaTypes)
Body.fromChunk(bc.codec(config).encode(value), mediaType)
val selected = codec.chooseFirstOrDefault(mediaTypes)
val mediaType = selected._1
val bc = selected._2
if (bc.schema == Schema[Unit]) Body.empty.contentType(mediaType)
else Body.fromChunk(bc.codec(config).encode(value), mediaType)
}

type Element = A
Expand Down Expand Up @@ -200,7 +208,9 @@ private[http] object BodyCodec {
)(implicit
trace: Trace,
): FormField = {
val (mediaType, bc) = codec.chooseFirstOrDefault(mediaTypes)
val selected = codec.chooseFirstOrDefault(mediaTypes)
val mediaType = selected._1
val bc = selected._2
FormField.streamingBinaryField(
name,
value >>> bc.codec(config).streamEncoder,
Expand All @@ -215,7 +225,9 @@ private[http] object BodyCodec {
)(implicit
trace: Trace,
): Body = {
val (mediaType, bc @ BinaryCodecWithSchema(_, _)) = codec.chooseFirstOrDefault(mediaTypes)
val selected = codec.chooseFirstOrDefault(mediaTypes)
val mediaType = selected._1
val bc = selected._2
Body.fromStreamChunked(value >>> bc.codec(config).streamEncoder).contentType(mediaType)
}

Expand All @@ -232,8 +244,8 @@ private[http] object BodyCodec {

private[internal] def validateZIO[A](schema: Schema[A])(e: A)(implicit trace: Trace): ZIO[Any, HttpCodecError, A] = {
val errors = Schema.validate(e)(schema)
if (errors.isEmpty) ZIO.succeed(e)
else ZIO.fail(HttpCodecError.InvalidEntity.wrap(errors))
if (errors.isEmpty) Exit.succeed(e)
else Exit.fail(HttpCodecError.InvalidEntity.wrap(errors))
}

private[internal] def validateStream[E](schema: Schema[E])(implicit
Expand Down

0 comments on commit c948d89

Please sign in to comment.