Skip to content

Commit

Permalink
Add QueryParamHint to hint on how many params codec expects
Browse files Browse the repository at this point in the history
  • Loading branch information
ekhov committed Jan 22, 2024
1 parent f875728 commit 8c376e2
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ private[cli] object CliEndpoint {
case HttpCodec.Path(pathCodec, _) =>
CliEndpoint(url = HttpOptions.Path(pathCodec) :: List())

case HttpCodec.Query(name, textCodec, _) =>
case HttpCodec.Query(name, textCodec, _, _) =>
textCodec.asInstanceOf[TextCodec[_]] match {
case TextCodec.Constant(value) => CliEndpoint(url = HttpOptions.QueryConstant(name, value) :: List())
case _ => CliEndpoint(url = HttpOptions.Query(name, textCodec) :: List())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import zio.test._
import zio.schema._

import zio.http._
import zio.http.codec.HttpCodec.Query.QueryParamHint
import zio.http.codec._
import zio.http.endpoint._
import zio.http.endpoint.cli.AuxGen._
Expand Down Expand Up @@ -93,7 +94,7 @@ object EndpointGen {
lazy val anyQuery: Gen[Any, CliReprOf[Codec[_]]] =
Gen.alphaNumericStringBounded(1, 30).zip(anyTextCodec).map { case (name, codec) =>
CliRepr(
HttpCodec.Query(name, codec),
HttpCodec.Query(name, codec, QueryParamHint.Any),
codec match {
case TextCodec.Constant(value) => CliEndpoint(url = HttpOptions.QueryConstant(name, value) :: Nil)
case _ => CliEndpoint(url = HttpOptions.Query(name, codec) :: Nil)
Expand Down
25 changes: 22 additions & 3 deletions zio-http/shared/src/main/scala/zio/http/codec/HttpCodec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ object HttpCodec extends ContentCodecs with HeaderCodecs with MethodCodecs with
}

private[http] final case class Status[A](codec: SimpleCodec[zio.http.Status, A], index: Int = 0)
extends Atom[HttpCodecType.Status, A] {
extends Atom[HttpCodecType.Status, A] {
self =>
def erase: Status[Any] = self.asInstanceOf[Status[Any]]

Expand Down Expand Up @@ -590,8 +590,12 @@ object HttpCodec extends ContentCodecs with HeaderCodecs with MethodCodecs with

def index(index: Int): ContentStream[A] = copy(index = index)
}
private[http] final case class Query[A](name: String, textCodec: TextCodec[A], index: Int = 0)
extends Atom[HttpCodecType.Query, Chunk[A]] {
private[http] final case class Query[A](
name: String,
textCodec: TextCodec[A],
hint: Query.QueryParamHint,
index: Int = 0,
) extends Atom[HttpCodecType.Query, Chunk[A]] {
self =>
def erase: Query[Any] = self.asInstanceOf[Query[Any]]

Expand All @@ -600,6 +604,21 @@ object HttpCodec extends ContentCodecs with HeaderCodecs with MethodCodecs with
def index(index: Int): Query[A] = copy(index = index)
}

private[http] object Query {

// Hint on how many query parameters codec expects
sealed trait QueryParamHint
object QueryParamHint {
case object One extends QueryParamHint

case object Many extends QueryParamHint

case object Zero extends QueryParamHint

case object Any extends QueryParamHint
}
}

private[http] final case class Method[A](codec: SimpleCodec[zio.http.Method, A], index: Int = 0)
extends Atom[HttpCodecType.Method, A] {
self =>
Expand Down
43 changes: 21 additions & 22 deletions zio-http/shared/src/main/scala/zio/http/codec/QueryCodecs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,37 @@
*/

package zio.http.codec
import zio.Chunk
import zio.stacktracer.TracingImplicits.disableAutoTrace
import zio.{Chunk, NonEmptyChunk}

import zio.http.codec.HttpCodec.Query.QueryParamHint

private[codec] trait QueryCodecs {

def query(name: String): QueryCodec[String] =
toSingleValue(name, HttpCodec.Query(name, TextCodec.string))
def query(name: String): QueryCodec[String] = singleValueCodec(name, TextCodec.string)

def queryBool(name: String): QueryCodec[Boolean] = singleValueCodec(name, TextCodec.boolean)

def queryBool(name: String): QueryCodec[Boolean] =
toSingleValue(name, HttpCodec.Query(name, TextCodec.boolean))
def queryInt(name: String): QueryCodec[Int] = singleValueCodec(name, TextCodec.int)

def queryInt(name: String): QueryCodec[Int] =
toSingleValue(name, HttpCodec.Query(name, TextCodec.int))
def queryTo[A](name: String)(implicit codec: TextCodec[A]): QueryCodec[A] = singleValueCodec(name, codec)

def queryTo[A](name: String)(implicit codec: TextCodec[A]): QueryCodec[A] =
toSingleValue(name, HttpCodec.Query(name, codec))
def queryAll(name: String): QueryCodec[Chunk[String]] = multiValueCodec(name, TextCodec.string)

def queryAll(name: String): QueryCodec[Chunk[String]] =
HttpCodec.Query(name, TextCodec.string)
def queryAllBool(name: String): QueryCodec[Chunk[Boolean]] = multiValueCodec(name, TextCodec.boolean)

def queryAllBool(name: String): QueryCodec[Chunk[Boolean]] =
HttpCodec.Query(name, TextCodec.boolean)
def queryAllInt(name: String): QueryCodec[Chunk[Int]] = multiValueCodec(name, TextCodec.int)

def queryAllInt(name: String): QueryCodec[Chunk[Int]] =
HttpCodec.Query(name, TextCodec.int)
def queryAllTo[A](name: String)(implicit codec: TextCodec[A]): QueryCodec[Chunk[A]] = multiValueCodec(name, codec)

def queryAllTo[A](name: String)(implicit codec: TextCodec[A]): QueryCodec[Chunk[A]] =
HttpCodec.Query(name, codec)
private def singleValueCodec[A](name: String, textCodec: TextCodec[A]): QueryCodec[A] =
HttpCodec
.Query(name, textCodec, QueryParamHint.One)
.transformOrFail {
case chunk if chunk.size == 1 => Right(chunk.head)
case chunk => Left(s"Expected single value for query parameter $name, but got ${chunk.size} instead")
}(s => Right(Chunk(s)))

private def toSingleValue[A](name: String, queryCodec: QueryCodec[Chunk[A]]): QueryCodec[A] =
queryCodec.transformOrFail {
case chunk if chunk.size == 1 => Right(chunk.head)
case chunk => Left(s"Expected single value for query parameter $name, but got ${chunk.size} instead")
}(s => Right(Chunk(s)))
private def multiValueCodec[A](name: String, textCodec: TextCodec[A]): QueryCodec[Chunk[A]] =
HttpCodec.Query(name, textCodec, QueryParamHint.Many)
}
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ object OpenAPIGen {
queryParams ++ pathParams ++ headerParams

def queryParams: Set[OpenAPI.ReferenceOr[OpenAPI.Parameter]] = {
inAtoms.query.collect { case mc @ MetaCodec(HttpCodec.Query(name, codec, _), _) =>
inAtoms.query.collect { case mc @ MetaCodec(HttpCodec.Query(name, codec, _, _), _) =>
OpenAPI.ReferenceOr.Or(
OpenAPI.Parameter.queryParameter(
name = name,
Expand Down

0 comments on commit 8c376e2

Please sign in to comment.