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

play.modules.swagger.SwaggerPlugin.scala Maven / Gradle / Ivy

/**
 * Copyright 2017 SmartBear Software, Inc.
 *
 * 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 play.modules.swagger

import java.io.File

import io.swagger.config.FilterFactory
import io.swagger.config.Scanner
import io.swagger.config.ScannerFactory
import io.swagger.core.filter.SwaggerSpecFilter
import javax.inject.Inject
import play.api.Configuration
import play.api.Environment
import play.api.Logger
import play.routes.compiler.RoutesFileParser
import play.routes.compiler.StaticPart
import play.routes.compiler.{Include => PlayInclude}
import play.routes.compiler.{Route => PlayRoute}

import scala.io.Source

trait SwaggerPlugin {
  def config: PlaySwaggerConfig
  def apiListingCache: ApiListingCache
  def playReader: PlayReader
  def scanner: Scanner
  def routes: RouteWrapper
  def swaggerSpecFilter: Option[SwaggerSpecFilter]
}

class SwaggerPluginImpl @Inject()(environment: Environment, configuration: Configuration) extends SwaggerPlugin {

  private val logger = Logger("swagger")

  lazy val config: PlaySwaggerConfig = PlaySwaggerConfig(configuration)

  lazy val routes = new RouteWrapper({
    val routesFile = configuration.get[Option[String]]("play.http.router") match {
      case None => "routes"
      case Some(value) => SwaggerPluginHelper.playRoutesClassNameToFileName(value)
    }

    val routesList = SwaggerPluginHelper.parseRoutes(routesFile, "", environment)

    Map(routesList map { route =>
      val call = route.call
      val routeName = (call.packageName.toSeq ++ Seq(call.controller + "$", call.method)).mkString(".")
      routeName -> route
    }: _*)
  })

  lazy val scanner = new PlayApiScanner(config, routes, environment)

  lazy val swaggerSpecFilter: Option[SwaggerSpecFilter] = config.filterClass match {
    case Some(e) if e.nonEmpty =>
      try {
        val filter = environment.classLoader.loadClass(e).getDeclaredConstructor().newInstance().asInstanceOf[SwaggerSpecFilter]
        logger.debug("Setting swagger.filter to %s".format(e))
        Some(filter)
      } catch {
        case ex: Exception =>
          Logger("swagger").error("Failed to load filter " + e, ex)
          None
      }
    case _ =>
      None
  }

  lazy val playReader = new PlayReader(config, routes, null)
  lazy val apiListingCache = new ApiListingCache(scanner, playReader)

  logger.info("Swagger: starting initialization")
  // eagerly initialize and set up global state required by Swagger
  swaggerSpecFilter foreach FilterFactory.setFilter
  ScannerFactory.setScanner(scanner)
  logger.info("Swagger: initialization done")
}

object SwaggerPluginHelper {
  private val logger = Logger("swagger")

  def playRoutesClassNameToFileName(className: String): String = className.replace(".Routes", ".routes")

  // Parses multiple route files recursively
  def parseRoutes(routesFile: String, prefix: String, env: Environment): List[PlayRoute] = {
    logger.debug(s"Processing route file '$routesFile' with prefix '$prefix'")

    val parsedRoutes = env.resourceAsStream(routesFile).map { stream =>
      val routesContent = Source.fromInputStream(stream).mkString
      RoutesFileParser.parseContent(routesContent, new File(routesFile))
    }.getOrElse(Right(List.empty)) // ignore routes files that don't exist

    val routes = parsedRoutes.getOrElse(throw new NoSuchElementException("Parsed routes not found!")).collect {
      case route: PlayRoute =>
        logger.debug(s"Adding route '$route'")
        (prefix, route.path.parts) match {
          case ("", _) => Seq(route)
          case (_, Seq()) => Seq(route.copy(path = route.path.copy(parts = StaticPart(prefix) +: route.path.parts)))
          case (_, Seq(StaticPart(""))) => Seq(route.copy(path = route.path.copy(parts = StaticPart(prefix) +: route.path.parts)))
          case (_, Seq(StaticPart("/"))) => Seq(route.copy(path = route.path.copy(parts = StaticPart(prefix) +: route.path.parts)))
          case (_, _) => Seq(route.copy(path = route.path.copy(parts = StaticPart(prefix) +: StaticPart("/") +: route.path.parts)))
        }
      case include: PlayInclude =>
        logger.debug(s"Processing route include $include")
        val newPrefix = if (prefix == "") {
          include.prefix
        } else {
          s"$prefix/${include.prefix}"
        }
        parseRoutes(playRoutesClassNameToFileName(include.router), newPrefix, env)
    }.flatten
    logger.debug(s"Finished processing route file '$routesFile'")
    routes
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy