Skip to content

Commit

Permalink
Schema based header codecs, unified with query codecs (#3232)
Browse files Browse the repository at this point in the history
Fix for publish CI job
  • Loading branch information
987Nabil committed Jan 9, 2025
1 parent 267c6ce commit 3fb0959
Show file tree
Hide file tree
Showing 28 changed files with 1,641 additions and 586 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ jobs:
tar xf targets.tar
rm targets.tar
- uses: coursier/setup-action@v1
with:
apps: sbt

- name: Release
env:
PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
Expand Down
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ ThisBuild / githubWorkflowTargetTags ++= Seq("v*")
ThisBuild / githubWorkflowPublishTargetBranches += RefPredicate.StartsWith(Ref.Tag("v"))
ThisBuild / githubWorkflowPublish :=
Seq(
WorkflowStep.Use(UseRef.Public("coursier", "setup-action", "v1"), Map("apps" -> "sbt")),
WorkflowStep.Sbt(
List("ci-release"),
name = Some("Release"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ServerInboundHandlerBenchmark {
private val largeString = random.alphanumeric.take(100000).mkString

private val baseUrl = "http://localhost:8080"
private val headers = Headers(Header.ContentType(MediaType.text.`plain`).untyped)
private val headers = Headers(Header.ContentType(MediaType.text.`plain`))

private val arrayEndpoint = "array"
private val arrayResponse = ZIO.succeed(
Expand Down
39 changes: 15 additions & 24 deletions zio-http-cli/src/main/scala/zio/http/endpoint/cli/CliEndpoint.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package zio.http.endpoint.cli

import zio.http._
import zio.http.codec.HttpCodec.Query.QueryType
import zio.http.codec._
import zio.http.endpoint._

Expand Down Expand Up @@ -112,13 +111,11 @@ private[cli] object CliEndpoint {
}
CliEndpoint(body = HttpOptions.Body(name, codec.defaultMediaType, codec.defaultSchema) :: List())

case HttpCodec.Header(name, textCodec, _) if textCodec.isInstanceOf[TextCodec.Constant] =>
CliEndpoint(headers =
HttpOptions.HeaderConstant(name, textCodec.asInstanceOf[TextCodec.Constant].string) :: List(),
)
case HttpCodec.Header(name, textCodec, _) =>
CliEndpoint(headers = HttpOptions.Header(name, textCodec) :: List())
case HttpCodec.Method(codec, _) =>
case HttpCodec.Header(headerType, _) =>
CliEndpoint(headers = HttpOptions.Header(headerType.name, TextCodec.string) :: List())
case HttpCodec.HeaderCustom(codec, _) =>
??? // todo
case HttpCodec.Method(codec, _) =>
codec.asInstanceOf[SimpleCodec[_, _]] match {
case SimpleCodec.Specified(method: Method) =>
CliEndpoint(methods = method)
Expand All @@ -128,22 +125,16 @@ private[cli] object CliEndpoint {
case HttpCodec.Path(pathCodec, _) =>
CliEndpoint(url = HttpOptions.Path(pathCodec) :: List())

case HttpCodec.Query(queryType, _) =>
queryType match {
case QueryType.Primitive(name, codec) =>
CliEndpoint(url = HttpOptions.Query(name, codec) :: List())
case record @ QueryType.Record(_) =>
val queryOptions = record.fieldAndCodecs.map { case (field, codec) =>
HttpOptions.Query(field.name, codec)
}
CliEndpoint(url = queryOptions.toList)
case QueryType.Collection(_, elements, _) =>
val queryOptions =
HttpOptions.Query(elements.name, elements.codec)
CliEndpoint(url = queryOptions :: List())
}

case HttpCodec.Status(_, _) => CliEndpoint.empty
case HttpCodec.Query(codec, _) =>
if (codec.isPrimitive)
CliEndpoint(url = HttpOptions.Query(codec) :: List())
else if (codec.isRecord)
CliEndpoint(url = codec.recordFields.map { case (_, codec) =>
HttpOptions.Query(codec)
}.toList)
else
CliEndpoint(url = HttpOptions.Query(codec) :: List())
case HttpCodec.Status(_, _) => CliEndpoint.empty

}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import zio.schema._
import zio.schema.annotation.description

import zio.http._
import zio.http.codec.HttpCodec.SchemaCodec
import zio.http.codec._

/*
Expand Down Expand Up @@ -264,10 +265,9 @@ private[cli] object HttpOptions {

}

final case class Query(override val name: String, codec: BinaryCodecWithSchema[_], doc: Doc = Doc.empty)
extends URLOptions {
final case class Query(codec: SchemaCodec[_], doc: Doc = Doc.empty) extends URLOptions {
self =>

override val name = codec.name.get
override val tag = "?" + name
def options: Options[_] = optionsFromSchema(codec)(name)

Expand All @@ -293,7 +293,7 @@ private[cli] object HttpOptions {

}

private[cli] def optionsFromSchema[A](codec: BinaryCodecWithSchema[A]): String => Options[A] =
private[cli] def optionsFromSchema[A](codec: SchemaCodec[A]): String => Options[A] =
codec.schema match {
case Schema.Primitive(standardType, _) =>
standardType match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ object CliSpec extends ZIOSpecDefault {

val bodyStream = ContentCodec.contentStream[BigInt]("bodyStream")

val headerCodec = HttpCodec.Header("header", TextCodec.string)
val headerCodec = HttpCodec.headerAs[String]("header")

val path1 = PathCodec.bool("path1")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,20 @@ object CommandGen {
case _: HttpOptions.Constant => false
case _ => true
}.map {
case HttpOptions.Path(pathCodec, _) =>
pathCodec.segments.toList.flatMap { case segment =>
case HttpOptions.Path(pathCodec, _) =>
pathCodec.segments.toList.flatMap { segment =>
getSegment(segment) match {
case (_, "") => Nil
case (name, "boolean") => s"[${getName(name, "")}]" :: Nil
case (name, codec) => s"${getName(name, "")} $codec" :: Nil
}
}
case HttpOptions.Query(name, codec, _) =>
getType(codec) match {
case "" => s"[${getName(name, "")}]" :: Nil
case codec => s"${getName(name, "")} $codec" :: Nil
case HttpOptions.Query(codec, _) if codec.isPrimitive =>
getType(codec.schema) match {
case "" => s"[${getName(codec.name.get, "")}]" :: Nil
case tpy => s"${getName(codec.name.get, "")} $tpy" :: Nil
}
case _ => Nil
case _ => Nil
}.foldRight(List[String]())(_ ++ _)

val headersOptions = cliEndpoint.headers.filter {
Expand Down Expand Up @@ -121,8 +121,8 @@ object CommandGen {
case _ => ""
}

def getType[A](codec: BinaryCodecWithSchema[A]): String =
codec.schema match {
def getType[A](schema: Schema[A]): String =
schema match {
case Schema.Primitive(standardType, _) =>
standardType match {
case StandardType.UnitType => ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import zio.test._

import zio.schema.Schema

import zio.http.Header.HeaderType
import zio.http._
import zio.http.codec.HttpCodec.SchemaCodec
import zio.http.codec._
import zio.http.endpoint._
import zio.http.endpoint.cli.AuxGen._
Expand Down Expand Up @@ -78,7 +80,7 @@ object EndpointGen {
lazy val anyHeader: Gen[Any, CliReprOf[Codec[_]]] =
Gen.alphaNumericStringBounded(1, 30).zip(anyTextCodec).map { case (name, codec) =>
CliRepr(
HttpCodec.Header(name, codec),
HttpCodec.Header(Header.Custom(name, "").headerType), // todo use schema bases header
codec match {
case TextCodec.Constant(value) => CliEndpoint(headers = HttpOptions.HeaderConstant(name, value) :: Nil)
case _ => CliEndpoint(headers = HttpOptions.Header(name, codec) :: Nil)
Expand All @@ -102,10 +104,10 @@ object EndpointGen {
lazy val anyQuery: Gen[Any, CliReprOf[Codec[_]]] =
Gen.alphaNumericStringBounded(1, 30).zip(anyStandardType).map { case (name, schema0) =>
val schema = schema0.asInstanceOf[Schema[Any]]
val codec = BinaryCodecWithSchema(TextBinaryCodec.fromSchema(schema), schema)
val codec = SchemaCodec(Some(name), schema)
CliRepr(
HttpCodec.Query(HttpCodec.Query.QueryType.Primitive(name, codec)),
CliEndpoint(url = HttpOptions.Query(name, codec) :: Nil),
HttpCodec.Query(codec),
CliEndpoint(url = HttpOptions.Query(codec) :: Nil),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import zio.test.Gen
import zio.schema.Schema

import zio.http._
import zio.http.codec.HttpCodec.SchemaCodec
import zio.http.codec._
import zio.http.endpoint.cli.AuxGen._
import zio.http.endpoint.cli.CliRepr._
Expand All @@ -32,10 +33,10 @@ object OptionsGen {
.optionsFromTextCodec(textCodec)(name)
.map(value => textCodec.encode(value))

def encodeOptions[A](name: String, codec: BinaryCodecWithSchema[A]): Options[String] =
def encodeOptions[A](name: String, codec: SchemaCodec[A]): Options[String] =
HttpOptions
.optionsFromSchema(codec)(name)
.map(value => codec.codec(CodecConfig.defaultConfig).encode(value).asString)
.map(value => codec.stringCodec.encode(value))

lazy val anyBodyOption: Gen[Any, CliReprOf[Options[Retriever]]] =
Gen
Expand Down Expand Up @@ -83,14 +84,12 @@ object OptionsGen {
},
Gen
.alphaNumericStringBounded(1, 30)
.zip(anyStandardType.map { s =>
val schema = s.asInstanceOf[Schema[Any]]
BinaryCodecWithSchema(TextBinaryCodec.fromSchema(schema), schema)
})
.map { case (name, codec) =>
.zip(anyStandardType)
.map { case (name, schema) =>
val codec = SchemaCodec(Some(name), schema)
CliRepr(
encodeOptions(name, codec),
CliEndpoint(url = HttpOptions.Query(name, codec) :: Nil),
CliEndpoint(url = HttpOptions.Query(codec) :: Nil),
)
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ object CodeGenSpec extends ZIOSpecDefault {
Endpoint(Method.GET / "api" / "v1" / "users")
.header(HeaderCodec.accept)
.header(HeaderCodec.contentType)
.header(HeaderCodec.name[String]("Token"))
.header(HeaderCodec.headerAs[String]("Token"))
val openAPI = OpenAPIGen.fromEndpoints(endpoint)

codeGenFromOpenAPI(openAPI) { testDir =>
Expand Down
Loading

0 comments on commit 3fb0959

Please sign in to comment.