Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Schema based query param access in QueryGetters\QueryModifier. #3202

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,26 @@ trait QueryGetters[+A] { self: QueryOps[A] =>
queryParameters.getAll(key)

/**
* Retrieves all typed query parameter values having the specified name.
* Retrieves all typed query parameter values having the specified name, with schema validation.
*/
def queryParamsTo[T](key: String)(implicit codec: TextCodec[T]): Either[QueryParamsError, Chunk[T]] =
def queryParamsTo[T](key: String, schema: Option[TextCodec[T]] = None)
(implicit codec: TextCodec[T]): Either[QueryParamsError, Chunk[T]] =
for {
params <- if (hasQueryParam(key)) Right(queryParams(key)) else Left(QueryParamsError.Missing(key))
(failed, typed) = params.partitionMap(p => codec.decode(p).toRight(p))
validatedCodec = schema.getOrElse(codec)
(failed, typed) = params.partitionMap(p => validatedCodec.decode(p).toRight(p))
result <- NonEmptyChunk
.fromChunk(failed)
.map(fails => QueryParamsError.Malformed(key, codec, fails))
.map(fails => QueryParamsError.Malformed(key, validatedCodec, fails))
.toLeft(typed)
} yield result

/**
* Retrieves all typed query parameter values having the specified name as
* ZIO.
* Retrieves all typed query parameter values having the specified name as ZIO, with schema validation.
*/
def queryParamsToZIO[T](key: String)(implicit codec: TextCodec[T]): IO[QueryParamsError, Chunk[T]] =
ZIO.fromEither(queryParamsTo[T](key))
def queryParamsToZIO[T](key: String, schema: Option[TextCodec[T]] = None)
(implicit codec: TextCodec[T]): IO[QueryParamsError, Chunk[T]] =
ZIO.fromEither(queryParamsTo[T](key, schema))

/**
* Retrieves the first query parameter value having the specified name.
Expand All @@ -58,44 +60,42 @@ trait QueryGetters[+A] { self: QueryOps[A] =>
if (hasQueryParam(key)) Some(queryParams(key).head) else None

/**
* Retrieves the first typed query parameter value having the specified name.
* Retrieves the first typed query parameter value having the specified name, with schema validation.
*/
def queryParamTo[T](key: String)(implicit codec: TextCodec[T]): Either[QueryParamsError, T] = for {
def queryParamTo[T](key: String, schema: Option[TextCodec[T]] = None)
(implicit codec: TextCodec[T]): Either[QueryParamsError, T] = for {
param <- queryParam(key).toRight(QueryParamsError.Missing(key))
typedParam <- codec.decode(param).toRight(QueryParamsError.Malformed(key, codec, NonEmptyChunk(param)))
validatedCodec = schema.getOrElse(codec)
typedParam <- validatedCodec.decode(param).toRight(QueryParamsError.Malformed(key, validatedCodec, NonEmptyChunk(param)))
} yield typedParam

/**
* Retrieves the first typed query parameter value having the specified name
* as ZIO.
* Retrieves the first typed query parameter value having the specified name as ZIO, with schema validation.
*/
def queryParamToZIO[T](key: String)(implicit codec: TextCodec[T]): IO[QueryParamsError, T] =
ZIO.fromEither(queryParamTo[T](key))
def queryParamToZIO[T](key: String, schema: Option[TextCodec[T]] = None)
(implicit codec: TextCodec[T]): IO[QueryParamsError, T] =
ZIO.fromEither(queryParamTo[T](key, schema))

/**
* Retrieves all query parameter values having the specified name, or else
* uses the default iterable.
* Retrieves all query parameter values having the specified name, or else uses the default iterable.
*/
def queryParamsOrElse(key: String, default: => Iterable[String]): Chunk[String] =
if (hasQueryParam(key)) queryParams(key) else Chunk.fromIterable(default)

/**
* Retrieves all query parameter values having the specified name, or else
* uses the default iterable.
* Retrieves all query parameter values having the specified name, or else uses the default iterable.
*/
def queryParamsToOrElse[T](key: String, default: => Iterable[T])(implicit codec: TextCodec[T]): Chunk[T] =
queryParamsTo[T](key).getOrElse(Chunk.fromIterable(default))

/**
* Retrieves the first query parameter value having the specified name, or
* else uses the default value.
* Retrieves the first query parameter value having the specified name, or else uses the default value.
*/
def queryParamOrElse(key: String, default: => String): String =
queryParam(key).getOrElse(default)

/**
* Retrieves the first typed query parameter value having the specified name,
* or else uses the default value.
* Retrieves the first typed query parameter value having the specified name, or else uses the default value.
*/
def queryParamToOrElse[T](key: String, default: => T)(implicit codec: TextCodec[T]): T =
queryParamTo[T](key).getOrElse(default)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,37 @@ package zio.http.internal
import zio.Chunk

import zio.http.QueryParams
import zio.http.codec.TextCodec

trait QueryModifier[+A] { self: QueryOps[A] with A =>

/**
* Combines two collections of query parameters together. If there are
* duplicate keys, the values from both sides are preserved, in order from
* left-to-right.
* Combines two collections of query parameters together. If there are duplicate keys,
* the values from both sides are preserved, in order from left-to-right.
*/
def ++(that: QueryParams): A =
updateQueryParams(params => QueryParams.fromEntries(params.seq ++ that.seq: _*))

/**
* Adds the specified key/value pair to the query parameters.
* Adds the specified key/value pair to the query parameters with optional schema validation.
*/
def addQueryParam(key: String, value: String): A =
addQueryParams(key, Chunk(value))
def addQueryParam[T](key: String, value: T, schema: Option[TextCodec[T]] = None)(implicit codec: TextCodec[T]): Either[QueryParamsError, A] = {
val validatedCodec = schema.getOrElse(codec)
validatedCodec.encode(value) match {
case Left(_) => Left(QueryParamsError.Malformed(key, validatedCodec, NonEmptyChunk(value.toString)))
case Right(encodedValue) => Right(addQueryParams(key, Chunk(encodedValue)))
}
}

/**
* Adds the specified key/value pairs to the query parameters.
* Adds the specified key/value pairs to the query parameters with optional schema validation.
*/
def addQueryParams(key: String, value: Chunk[String]): A =
updateQueryParams(params => params ++ QueryParams(key -> value))
def addQueryParams[T](key: String, values: Chunk[T], schema: Option[TextCodec[T]] = None)(implicit codec: TextCodec[T]): Either[QueryParamsError, A] = {
val validatedCodec = schema.getOrElse(codec)
val encodedValues = values.map(validatedCodec.encode(_).toOption.getOrElse(""))

def addQueryParams(values: String): A =
updateQueryParams(params => params ++ QueryParams.decode(values))
Right(updateQueryParams(params => params ++ QueryParams(key -> encodedValues)))
}

/**
* Removes the specified key from the query parameters.
Expand Down
Loading