All Downloads are FREE. Search and download functionalities are using the official Maven repository.

endpoints4s.algebra.Requests.scala Maven / Gradle / Ivy

The newest version!
package endpoints4s.algebra

import endpoints4s._

/** @group algebras
  */
trait Requests extends Urls with Methods with SemigroupalSyntax {

  /** Information carried by requests’ headers.
    *
    * You can construct values of type `RequestHeaders` by using the operations
    * [[requestHeader]], [[optRequestHeader]], or [[emptyRequestHeaders]].
    *
    *   - Server interpreters raise an error if they can’t parse the incoming
    *     request headers as a value of type `A`. By default,
    *     they produce a Bad Request (400) response with a list of error messages
    *     in a JSON array. Refer to the documentation of your server interpreter
    *     to customize this behavior.
    *
    * @note  This type has implicit methods provided by the [[SemigroupalSyntax]]
    *        and [[PartialInvariantFunctorSyntax]] classes.
    * @group types
    */
  type RequestHeaders[A]

  /** Ignore headers
    *
    *   - Server interpreters don’t try to parse any information from the
    *     request headers,
    *   - Client interpreters supply no specific headers
    *
    * Use `description` of [[endpoints4s.algebra.Endpoints#endpoint]] to document empty headers.
    *
    * @group operations
    */
  def emptyRequestHeaders: RequestHeaders[Unit]

  /** A required request header
    * @param name Header name (e.g., “Authorization”)
    * @group operations
    */
  def requestHeader(
      name: String,
      docs: Documentation = None
  ): RequestHeaders[String]

  /** An optional request header
    * @param name Header name (e.g., “Authorization”)
    * @group operations
    */
  def optRequestHeader(
      name: String,
      docs: Documentation = None
  ): RequestHeaders[Option[String]]

  /** Provides `++` operation.
    * @see [[SemigroupalSyntax]]
    */
  implicit def requestHeadersSemigroupal: Semigroupal[RequestHeaders]

  /** Provides the operations `xmap` and `xmapPartial`.
    * @see [[PartialInvariantFunctorSyntax]]
    */
  implicit def requestHeadersPartialInvariantFunctor: PartialInvariantFunctor[RequestHeaders]

  /** Information carried by a whole request (headers and entity)
    *
    * Values of type `Request[A]` can be constructed by using the operations
    * [[request]], [[get]], [[post]], [[put]], or [[delete]].
    *
    *   - Server interpreters raise an error if they can’t parse the incoming
    *     request as a value of type `A`. By default,
    *     they produce a Bad Request (400) response with a list of error messages
    *     in a JSON array. Refer to the documentation of your server interpreter
    *     to customize this behavior.
    *
    * @note This type has implicit methods provided by the [[PartialInvariantFunctorSyntax]]
    *       and [[RequestSyntax]] classes.
    * @group types
    */
  type Request[A]

  /** Provides the operations `xmap` and `xmapPartial`.
    * @see [[PartialInvariantFunctorSyntax]]
    */
  implicit def requestPartialInvariantFunctor: PartialInvariantFunctor[Request]

  /** Information carried by request entity
    *
    * Values of type `RequestEntity[A]` can be constructed by using the operations
    * [[emptyRequest]] or [[textRequest]]. Additional types of request entities
    * are provided by other algebra modules, such as [[endpoints4s.algebra.JsonEntities JsonEntities]]
    * or [[endpoints4s.algebra.ChunkedEntities ChunkedEntities]].
    *
    *   - Server interpreters raise an error if they can’t parse the incoming
    *     request entity as a value of type `A`. By default,
    *     they produce a Bad Request (400) response with a list of error messages
    *     in a JSON array. Refer to the documentation of your server interpreter
    *     to customize this behavior.
    *
    * @note This type has implicit methods provided by the [[PartialInvariantFunctorSyntax]] and
    *       [[RequestEntitySyntax]] classes.
    * @group types
    */
  type RequestEntity[A]

  /** Provides the operations `xmap` and `xmapPartial`.
    * @see [[PartialInvariantFunctorSyntax]]
    */
  implicit def requestEntityPartialInvariantFunctor: PartialInvariantFunctor[RequestEntity]

  /** Empty request -- request without a body.
    * Use `description` of [[endpoints4s.algebra.Endpoints#endpoint]] to document an empty body.
    *
    * @group operations
    */
  def emptyRequest: RequestEntity[Unit]

  /** Request with a `String` body.
    *
    *   - Server interpreters accept requests with content-type `text/plain` and
    *     reject requests with an incorrect content-type.
    *   - Server interpreters will use the character encoding set in the
    *     content-type header to determine how the text is decoded.
    *   - Client interpreters supply content-type `text/plain` with an explicit
    *     character encoding
    *
    * @group operations
    */
  def textRequest: RequestEntity[String]

  /** Alternative between two possible request entities, differentiated by the
    * `Content-Type` header
    *
    * @note If `A` and `B` are both JSON-encoded and use disjoint schemas, use
    *       [[endpoints4s.algebra.JsonSchemas.TaggedOps#orElse]] at the schema level instead
    *
    *   - Server interpreters accept either of the request entities
    *   - Client interpreters provide one of the two request entities
    *   - Documentation interpreters list all possible content types and their entities
    */
  def choiceRequestEntity[A, B](
      requestEntityA: RequestEntity[A],
      requestEntityB: RequestEntity[B]
  ): RequestEntity[Either[A, B]]

  implicit class RequestEntitySyntax[A](requestEntity: RequestEntity[A]) {

    /** Alternative between two possible request entities, differentiated by the
      * `Content-Type` header
      *
      * @note If `A` and `B` are both JSON-encoded and use disjoint schemas, use
      *       [[endpoints4s.algebra.JsonSchemas.TaggedOps#orElse]] at the schema level instead
      *
      *   - Server interpreters accept either of the request entities
      *   - Client interpreters provide one of the two request entities
      *   - Documentation interpreters list all possible content types and their entities
      */
    final def orElse[B](
        otherRequestEntity: RequestEntity[B]
    ): RequestEntity[Either[A, B]] =
      choiceRequestEntity(requestEntity, otherRequestEntity)
  }

  /** Request for given parameters
    *
    * @param method Request method
    * @param url Request URL
    * @param entity Request entity
    * @param docs Request documentation
    * @param headers Request headers
    * @tparam UrlP Payload carried by url
    * @tparam BodyP Payload carried by body
    * @tparam HeadersP Payload carried by headers
    * @tparam UrlAndBodyPTupled Payloads of Url and Body tupled together by [[Tupler]]
    * @group operations
    */
  def request[UrlP, BodyP, HeadersP, UrlAndBodyPTupled, Out](
      method: Method,
      url: Url[UrlP],
      entity: RequestEntity[BodyP] = emptyRequest,
      docs: Documentation = None,
      headers: RequestHeaders[HeadersP] = emptyRequestHeaders
  )(implicit
      tuplerUB: Tupler.Aux[UrlP, BodyP, UrlAndBodyPTupled],
      tuplerUBH: Tupler.Aux[UrlAndBodyPTupled, HeadersP, Out]
  ): Request[Out]

  /** Add the provided `headers` to the `request`.
    */
  def addRequestHeaders[A, H](
      request: Request[A],
      headers: RequestHeaders[H]
  )(implicit tupler: Tupler[A, H]): Request[tupler.Out] =
    unsupportedInterpreter("1.6.0")

  /** Add the provided `queryString` to the `request`.
    */
  def addRequestQueryString[A, Q](
      request: Request[A],
      queryString: QueryString[Q]
  )(implicit tupler: Tupler[A, Q]): Request[tupler.Out] =
    unsupportedInterpreter("1.6.0")

  /** Helper method to perform GET request
    * @tparam UrlP Payload carried by url
    * @tparam HeadersP Payload carried by headers
    * @group operations
    */
  final def get[UrlP, HeadersP, Out](
      url: Url[UrlP],
      docs: Documentation = None,
      headers: RequestHeaders[HeadersP] = emptyRequestHeaders
  )(implicit tuplerUH: Tupler.Aux[UrlP, HeadersP, Out]): Request[Out] =
    request(Get, url, docs = docs, headers = headers)

  /** Helper method to perform POST request
    * @param docs Request documentation
    * @tparam UrlP Payload carried by url
    * @tparam BodyP Payload carried by body
    * @tparam HeadersP Payload carried by headers
    * @tparam UrlAndBodyPTupled Payloads of Url and Body tupled together by [[Tupler]]
    * @group operations
    */
  final def post[UrlP, BodyP, HeadersP, UrlAndBodyPTupled, Out](
      url: Url[UrlP],
      entity: RequestEntity[BodyP],
      docs: Documentation = None,
      headers: RequestHeaders[HeadersP] = emptyRequestHeaders
  )(implicit
      tuplerUB: Tupler.Aux[UrlP, BodyP, UrlAndBodyPTupled],
      tuplerUBH: Tupler.Aux[UrlAndBodyPTupled, HeadersP, Out]
  ): Request[Out] =
    request(Post, url, entity, docs, headers)

  /** Helper method to perform PUT request
    * @tparam UrlP Payload carried by url
    * @tparam BodyP Payload carried by body
    * @tparam HeadersP Payload carried by headers
    * @tparam UrlAndBodyPTupled Payloads of Url and Body tupled together by [[Tupler]]
    * @group operations
    */
  final def put[UrlP, BodyP, HeadersP, UrlAndBodyPTupled, Out](
      url: Url[UrlP],
      entity: RequestEntity[BodyP],
      docs: Documentation = None,
      headers: RequestHeaders[HeadersP] = emptyRequestHeaders
  )(implicit
      tuplerUB: Tupler.Aux[UrlP, BodyP, UrlAndBodyPTupled],
      tuplerUBH: Tupler.Aux[UrlAndBodyPTupled, HeadersP, Out]
  ): Request[Out] =
    request(Put, url, entity, docs, headers)

  /** Helper method to perform DELETE request
    * @tparam UrlP Payload carried by url
    * @tparam HeadersP Payload carried by headers
    * @group operations
    */
  final def delete[UrlP, HeadersP, Out](
      url: Url[UrlP],
      docs: Documentation = None,
      headers: RequestHeaders[HeadersP] = emptyRequestHeaders
  )(implicit tuplerUH: Tupler.Aux[UrlP, HeadersP, Out]): Request[Out] =
    request(Delete, url, docs = docs, headers = headers)

  /** Helper method to perform PATCH request
    * @param docs Request documentation
    * @tparam UrlP Payload carried by url
    * @tparam BodyP Payload carried by body
    * @tparam HeadersP Payload carried by headers
    * @tparam UrlAndBodyPTupled Payloads of Url and Body tupled together by [[Tupler]]
    * @group operations
    */
  final def patch[UrlP, BodyP, HeadersP, UrlAndBodyPTupled, Out](
      url: Url[UrlP],
      entity: RequestEntity[BodyP],
      docs: Documentation = None,
      headers: RequestHeaders[HeadersP] = emptyRequestHeaders
  )(implicit
      tuplerUB: Tupler.Aux[UrlP, BodyP, UrlAndBodyPTupled],
      tuplerUBH: Tupler.Aux[UrlAndBodyPTupled, HeadersP, Out]
  ): Request[Out] =
    request(Patch, url, entity, docs, headers)

  /** Extension methods for [[Request]].
    *
    * @group operations
    */
  implicit final class RequestSyntax[A](request: Request[A]) {

    def addHeaders[H](headers: RequestHeaders[H])(implicit
        tupler: Tupler[A, H]
    ): Request[tupler.Out] =
      addRequestHeaders(request, headers)

    def addQueryString[Q](qs: QueryString[Q])(implicit
        tupler: Tupler[A, Q]
    ): Request[tupler.Out] =
      addRequestQueryString(request, qs)
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy