com.github.swagger.pekko.SwaggerHttpService.scala Maven / Gradle / Ivy
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.swagger.pekko
import org.apache.pekko.http.scaladsl.model.{HttpEntity, MediaTypes}
import org.apache.pekko.http.scaladsl.server.{Directives, PathMatchers, Route}
import com.github.swagger.pekko.model.{Info, asScala}
import io.swagger.v3.core.util.{Json, Json31, Yaml, Yaml31}
import io.swagger.v3.jaxrs2.Reader
import io.swagger.v3.oas.integration.SwaggerConfiguration
import io.swagger.v3.oas.models.security.{SecurityRequirement, SecurityScheme}
import io.swagger.v3.oas.models.servers.Server
import io.swagger.v3.oas.models.{Components, ExternalDocumentation, OpenAPI, SpecVersion}
import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory
import scala.collection.JavaConverters._
import scala.collection.mutable.{ListBuffer, Map => MutableMap}
import scala.util.control.NonFatal
object SwaggerHttpService {
def readerConfig = new SwaggerConfiguration
def removeInitialSlashIfNecessary(path: String): String =
if(path.startsWith("/")) removeInitialSlashIfNecessary(path.substring(1)) else path
def removeTrailingSlashIfNecessary(path: String): String =
if(path.endsWith("/")) removeTrailingSlashIfNecessary(path.substring(0, path.length)) else path
def prependSlashIfNecessary(path: String): String = if(path.startsWith("/")) path else s"/$path"
private[pekko] def apiDocsBase(path: String) = PathMatchers.separateOnSlashes(removeInitialSlashIfNecessary(path))
private[pekko] val logger = LoggerFactory.getLogger(classOf[SwaggerHttpService])
}
trait SwaggerGenerator {
import SwaggerHttpService._
def apiClasses: Set[Class[_]]
/**
* @return the host (and possibly, port) for the API (eg `api.example.com` or `localhost:8080`)
* - used in conjunction with the [[schemes]] - this is ignored if you have non-empty [[serverURLs]]
*/
def host: String = ""
/**
* @return list of URL schemes (default is `["http"]`) - this is ignored if you have non-empty [[serverURLs]]
*/
def schemes: List[String] = List("http")
/**
* @return list of URLs (as strings) - if this list is empty, then the values are derived using the [[SwaggerGenerator.host]] and [[schemes]]
*/
def serverURLs: Seq[String] = Seq.empty
def basePath: String = ""
def apiDocsPath: String = "api-docs"
def info: Info = Info()
def components: Option[Components] = None
def security: List[SecurityRequirement] = List.empty
def securitySchemes: Map[String, SecurityScheme] = Map.empty
def externalDocs: Option[ExternalDocumentation] = None
def vendorExtensions: Map[String, Object] = Map.empty
def unwantedDefinitions: Seq[String] = Seq.empty
def specVersion: SpecVersion = SpecVersion.V30
def swaggerConfig: OpenAPI = {
val swagger = new OpenAPI()
val sv = specVersion
swagger.setSpecVersion(sv)
val version = if (sv == SpecVersion.V31) "3.1.0" else "3.0.1"
swagger.setOpenapi(version)
import model._
swagger.setInfo(info)
components.foreach { c => swagger.setComponents(c) }
val path = removeInitialSlashIfNecessary(basePath)
serverURLs match {
case Seq() => {
val hostPath = if (StringUtils.isNotBlank(path)) {
s"${removeTrailingSlashIfNecessary(host)}/${path}/"
} else {
host
}
schemes.foreach { scheme =>
swagger.addServersItem(new Server().url(s"${scheme.toLowerCase}://$hostPath"))
}
if (schemes.isEmpty && StringUtils.isNotBlank(hostPath)) {
swagger.addServersItem(new Server().url(hostPath))
}
}
case urlSeq => {
urlSeq.foreach { url =>
val urlPath = if (StringUtils.isNotBlank(path)) {
s"${removeTrailingSlashIfNecessary(url)}/${path}/"
} else {
url
}
swagger.addServersItem(new Server().url(urlPath))
}
}
}
securitySchemes.foreach { case (k: String, v: SecurityScheme) => swagger.schemaRequirement(k, v) }
swagger.setSecurity(asJavaMutableList(security))
swagger.extensions(asJavaMutableMap(vendorExtensions))
externalDocs.foreach { ed => swagger.setExternalDocs(ed) }
swagger
}
def reader = {
val config = readerConfig
if (specVersion == SpecVersion.V31) {
config.setOpenAPI31(true)
}
new Reader(config.openAPI(swaggerConfig))
}
def generateSwaggerJson: String = {
try {
val objectWriter = if (specVersion == SpecVersion.V31) Json31.pretty() else Json.pretty()
objectWriter.writeValueAsString(filteredSwagger)
} catch {
case NonFatal(t) => {
logger.error("Issue with creating swagger.json", t)
throw t
}
}
}
def generateSwaggerYaml: String = {
try {
val objectWriter = if (specVersion == SpecVersion.V31) Yaml31.pretty() else Yaml.pretty()
objectWriter.writeValueAsString(filteredSwagger)
} catch {
case NonFatal(t) => {
logger.error("Issue with creating swagger.yaml", t)
throw t
}
}
}
private[pekko] def asJavaMutableList[T](list: List[T]) = {
(new ListBuffer[T] ++ list).asJava
}
private[pekko] def asJavaMutableMap[K, V](map: Map[K, V]) = {
(MutableMap.empty[K, V] ++ map).asJava
}
private[pekko] def filteredSwagger: OpenAPI = {
val swagger: OpenAPI = reader.read(apiClasses.asJava)
Option(swagger.getComponents) match {
case Some(components) => {
if (!unwantedDefinitions.isEmpty) {
val filteredSchemas = asJavaMutableMap(asScala(components.getSchemas).filterKeys(
definitionName => !unwantedDefinitions.contains(definitionName)).toMap)
components.setSchemas(filteredSchemas)
}
}
case _ =>
}
swagger
}
}
trait SwaggerHttpService extends Directives with SwaggerGenerator {
import SwaggerHttpService._
def routes: Route = {
val base = apiDocsBase(apiDocsPath)
path(base / "swagger.json") {
get {
complete(HttpEntity(MediaTypes.`application/json`, generateSwaggerJson))
}
} ~
path(base / "swagger.yaml") {
get {
complete(HttpEntity(CustomMediaTypes.`text/vnd.yaml`, generateSwaggerYaml))
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy