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

harness.http.server.Endpoint.scala Maven / Gradle / Ivy

There is a newer version: 5.1.3
Show newest version
package harness.http.server

import harness.endpoint.error.DecodingFailure
import harness.endpoint.spec.*
import harness.endpoint.transfer.*
import harness.endpoint.typeclass.*
import harness.endpoint.types.*
import harness.endpoint.types.Types.*
import harness.zio.{Path as _, *}
import zio.*

final case class Endpoint[-R, ET <: EndpointType.Any](
    spec: EndpointSpec[ET],
    implementation: Implementation[R, ET],
) {

  private val errorHandler = implementation.errorHandler

  private def decode(request: HttpRequest): IO[DecodingFailure, (Query[ET], Auth[ET], Header[ET], Receive[InputBody[ET]])] =
    for {
      q <- ZIO.fromEither { spec.queryCodec.decode(request.queries) }
      a <- ZIO.fromEither { spec.authHeaderCodec.decode(request.headers, request.cookies) }
      h <- ZIO.fromEither { spec.headerCodec.decode(request.headers, request.cookies) }
      b <- spec.inputBodyCodec.in(request.contentLengths, request.rawInputStream)
    } yield (q, a, h, b)

  def handleScoped(
      request: HttpRequest,
      parsedPath: Path[ET],
  ): ZIO[HarnessEnv & Scope & R, implementation.DomainError, HttpResponse[Send[OutputBody[ET]]]] =
    Logger.log.debug(s"Headers:${request.headers.keySet.toSeq.sorted.map(h => s"\n  - $h").mkString}") *>
      decode(request)
        .mapError(errorHandler.convertDecodingFailure)
        .flatMap { implementation.impl(parsedPath, _, _, _, _) }
        .provideSomeEnvironment[HarnessEnv & Scope & R](_ ++ ZEnvironment(request))

  def handle(
      request: HttpRequest,
      parsedPath: Path[ET],
  ): ZIO[HarnessEnv & R, implementation.DomainError, HttpResponse[Send[OutputBody[ET]]]] =
    ZIO.scoped { handleScoped(request, parsedPath) }

  def handleRaw(
      request: HttpRequest,
      parsedPath: Path[ET],
  ): ZIO[HarnessEnv & R, implementation.DomainError, HttpResponse[OutputStream]] =
    handle(request, parsedPath).map { result => result.copy(body = spec.outputBodyCodec.out(result.body)) }

  def parseAndHandle(
      request: HttpRequest,
  ): ZIO[HarnessEnv & R, implementation.DomainError, Option[HttpResponse[Send[OutputBody[ET]]]]] =
    spec.pathCodec.decodePath(request.path) match {
      case Some(parsedPath) => handle(request, parsedPath).asSome
      case None             => ZIO.none
    }

}
object Endpoint {

  type Projection[R] = [ET <: EndpointType.Any] =>> Endpoint[R, ET]

  def make[R, T[_[_ <: EndpointType.Any]]](
      spec: T[EndpointSpec],
      impl: T[Implementation.Projection[R]],
  )(using
      zip: Zipper[T],
      mapK: MapK[T],
  ): T[Endpoint.Projection[R]] =
    mapK.mapK(Zipper.zip(spec, impl)) { [t <: EndpointType.Any] => (a: K11ET.Zip[EndpointSpec, Implementation.Projection[R]][t]) => Endpoint(a._1, a._2) }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy