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

sttp.tapir.redoc.Redoc.scala Maven / Gradle / Ivy

There is a newer version: 1.11.10
Show newest version
package sttp.tapir.redoc

import sttp.model.{Header, HeaderNames, MediaType, StatusCode}
import sttp.tapir._
import sttp.tapir.server.ServerEndpoint

object Redoc {

  val defaultRedocVersion = "2.0.0-rc.56"

  def redocHtml(title: String, specUrl: String, redocVersion: String = defaultRedocVersion): String = s"""
    |
    |
    |
    |  $title
    |  
    |  
    |  
    |  
    |
    |  
    |  
    |
    |
    |
    |
    |
    |
    |""".stripMargin

  def apply[F[_]](
      title: String,
      spec: String,
      options: RedocUIOptions
  ): List[ServerEndpoint[Any, F]] = {
    val specName = options.specName
    val htmlName = options.htmlName

    val prefixInput: EndpointInput[Unit] = options.pathPrefix.map(stringToPath).foldLeft(emptyInput)(_.and(_))

    val baseEndpoint = infallibleEndpoint.get.in(prefixInput)
    val redirectOutput = statusCode(StatusCode.PermanentRedirect).and(header[String](HeaderNames.Location))

    def contentEndpoint(fileName: String, mediaType: MediaType) =
      baseEndpoint.in(fileName).out(stringBody).out(header(Header.contentType(mediaType)))

    val specNameLowerCase = specName.toLowerCase
    val specMediaType =
      if (specNameLowerCase.endsWith(".json")) MediaType("application", "json")
      else if (specNameLowerCase.endsWith(".yaml") || specNameLowerCase.endsWith(".yml")) MediaType("text", "yaml")
      else MediaType("text", "plain")

    val specEndpoint = contentEndpoint(specName, specMediaType).serverLogicPure[F](_ => Right(spec))

    val specPrefix = if (options.useRelativePaths) "." else "/" + (options.contextPath ++ options.pathPrefix).mkString("/")
    val html: String = redocHtml(title, s"$specPrefix/$specName", options.redocVersion)
    val htmlEndpoint = contentEndpoint(htmlName, MediaType.TextHtml).serverLogicPure[F](_ => Right(html))

    val lastSegmentInput: EndpointInput[Option[String]] = extractFromRequest(_.uri.path.lastOption)

    val redirectToHtmlEndpoint =
      baseEndpoint
        .in(lastSegmentInput)
        .out(redirectOutput)
        .serverLogicPure[F] { lastSegment =>
          if (options.useRelativePaths) {
            val pathFromLastSegment: String = lastSegment match {
              case Some(s) if s.nonEmpty => s + "/"
              case _                     => ""
            }
            Right(s"./$pathFromLastSegment$htmlName")
          } else
            Right(options.contextPath ++ options.pathPrefix match {
              case Nil      => s"/$htmlName"
              case segments => s"/${segments.mkString("/")}/$htmlName"
            })
        }

    List(specEndpoint, htmlEndpoint, redirectToHtmlEndpoint)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy