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.9
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,
      redocOptions: Option[String] = None,
      redocThemeOptionsJson: Option[String] = None
  ): String = {
    val options = redocOptions.filterNot(_.isEmpty).getOrElse("")
    val themeOptions = redocThemeOptionsJson.filterNot(_.isEmpty).fold("")(json => s"theme='$json'")
    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).serverLogicSuccessPure[F](_ => spec)

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

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

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

    List(specEndpoint, htmlEndpoint, redirectToHtmlEndpoint)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy