Rest DSL. camel-swagger work in progress.
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/b234476a Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/b234476a Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/b234476a Branch: refs/heads/master Commit: b234476abf46ddf1f109fe3fbc1b6395c0ae5228 Parents: a578af9 Author: Claus Ibsen <davscl...@apache.org> Authored: Sat Aug 9 11:51:52 2014 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Sat Aug 9 12:31:18 2014 +0200 ---------------------------------------------------------------------- .../component/swagger/RestApiListingCache.scala | 54 +++++++++ .../RestSwaggerApiDeclarationServlet.scala | 117 +++++++++++++------ .../component/swagger/RestSwaggerReader.scala | 30 ++--- .../src/main/webapp/WEB-INF/web.xml | 34 +++--- 4 files changed, 164 insertions(+), 71 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/b234476a/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestApiListingCache.scala ---------------------------------------------------------------------- diff --git a/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestApiListingCache.scala b/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestApiListingCache.scala new file mode 100644 index 0000000..494e620 --- /dev/null +++ b/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestApiListingCache.scala @@ -0,0 +1,54 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.component.swagger + +import com.wordnik.swagger.core.util.ReaderUtil +import com.wordnik.swagger.config.SwaggerConfig +import org.apache.camel.CamelContext +import com.wordnik.swagger.model.{Operation, ApiListing} +import scala.collection.mutable.ListBuffer + +// to iterate Java list using for loop +import scala.collection.JavaConverters._ + +object RestApiListingCache extends ReaderUtil { + + var cache: Option[Map[String, ApiListing]] = None + val reader = new RestSwaggerReader() + + def listing(camel: CamelContext, config: SwaggerConfig): Option[Map[String, ApiListing]] = { + cache.orElse { + val listings = new ListBuffer[ApiListing] + + val rests = camel.getRestDefinitions.asScala + for (rest <- rests) { + // TODO: this can be nicer + val option = reader.read(rest, config) + if (option.get != null) { + listings += option.get + } + } + + if (listings.size > 0) { + val mergedListings = groupByResourcePath(listings.toList) + cache = Some(mergedListings.map(m => (m.resourcePath, m)).toMap) + } + cache + } + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/b234476a/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestSwaggerApiDeclarationServlet.scala ---------------------------------------------------------------------- diff --git a/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestSwaggerApiDeclarationServlet.scala b/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestSwaggerApiDeclarationServlet.scala index 0cb985f..11af449 100644 --- a/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestSwaggerApiDeclarationServlet.scala +++ b/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestSwaggerApiDeclarationServlet.scala @@ -16,55 +16,99 @@ */ package org.apache.camel.component.swagger -import com.wordnik.swagger.servlet.listing.{ApiListingCache, ApiDeclarationServlet} -import javax.servlet.http.{HttpServletResponse, HttpServletRequest} +import javax.servlet.http.{HttpServlet, HttpServletResponse, HttpServletRequest} +import javax.servlet.ServletConfig + +import com.wordnik.swagger.core.SwaggerContext import com.wordnik.swagger.core.filter.SpecFilter -import com.wordnik.swagger.config.{ConfigFactory, FilterFactory} -import com.wordnik.swagger.model.{ResourceListing, ApiListingReference} import com.wordnik.swagger.core.util.JsonSerializer -import javax.servlet.ServletConfig +import com.wordnik.swagger.config.{SwaggerConfig, ConfigFactory, FilterFactory} +import com.wordnik.swagger.model.{ApiInfo, ResourceListing, ApiListingReference} + import org.springframework.web.context.support.WebApplicationContextUtils import org.springframework.web.context.WebApplicationContext import org.apache.camel.CamelContext import org.slf4j.LoggerFactory -class RestSwaggerApiDeclarationServlet extends ApiDeclarationServlet { +class RestSwaggerApiDeclarationServlet extends HttpServlet { + + // TODO: this has spring dependency, find a way to make it work with blueprint/spring/cdi/servlet-listener etc private val LOG = LoggerFactory.getLogger(classOf[RestSwaggerApiDeclarationServlet]) var spring: WebApplicationContext = null val reader = new RestSwaggerReader() + var camel: CamelContext = null + val swaggerConfig: SwaggerConfig = ConfigFactory.config override def init(config: ServletConfig): Unit = { super.init(config) LOG.info("init") + + // configure swagger options + var s = config.getInitParameter("api.version") + if (s != null) { + swaggerConfig.setApiVersion(s) + } + s = config.getInitParameter("swagger.version") + if (s != null) { + swaggerConfig.setSwaggerVersion(s) + } + s = config.getInitParameter("base.path") + if (s != null) { + swaggerConfig.setBasePath(s) + } + s = config.getInitParameter("api.path") + if (s != null) { + swaggerConfig.setApiPath(s) + } + + val title = config.getInitParameter("api.title") + val description = config.getInitParameter("api.description") + val termsOfServiceUrl = config.getInitParameter("api.termsOfServiceUrl") + val contact = config.getInitParameter("api.contact") + val license = config.getInitParameter("api.license") + val licenseUrl = config.getInitParameter("api.licenseUrl") + + val apiInfo = new ApiInfo(title, description, termsOfServiceUrl, contact, license, licenseUrl) + swaggerConfig.setApiInfo(apiInfo) + spring = WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext) + if (spring != null) { + camel = spring.getBean(classOf[CamelContext]) + if (camel != null) { + // TODO: if this is not sufficient we need to use Camel's resolveClass API instead + SwaggerContext.registerClassLoader(camel.getApplicationContextClassLoader) + } + } + LOG.info("init found spring {}", spring) } - override def renderResourceListing(request: HttpServletRequest, response: HttpServletResponse) = { + override protected def doGet(request: HttpServletRequest, response: HttpServletResponse) = { + val route = request.getPathInfo + // render overview if the route is empty or is the root path + if (route != null && route != "" && route != "/") { + renderApiDeclaration(request, response) + } else { + renderResourceListing(request, response) + } + } + + /** + * Renders the resource listing which is the overview of all the apis + */ + def renderResourceListing(request: HttpServletRequest, response: HttpServletResponse) = { LOG.info("renderResourceListing") - val docRoot = "" val queryParams = Map[String, List[String]]() val cookies = Map[String, String]() val headers = Map[String, List[String]]() - val camel = spring.getBean(classOf[CamelContext]) LOG.info("renderResourceListing camel -> {}", camel) if (camel != null) { - - val config = ConfigFactory.config - val rests = camel.getRestDefinitions - LOG.info("renderResourceListing rests -> {}", rests) - val list = reader.read(rests.get(0), config) - LOG.info("renderResourceListing reader -> {}", list) - val cache = Some(list.map(m => (m.resourcePath, m)).toMap) - LOG.info("renderResourceListing reader -> {}", cache) - val f = new SpecFilter - // val listings = ApiListingCache.listing(docRoot).map(specs => { - val listings = cache.map(specs => { + val listings = RestApiListingCache.listing(camel, swaggerConfig).map(specs => { (for (spec <- specs.values) yield f.filter(spec, FilterFactory.filter, queryParams, cookies, headers) ).filter(m => m.apis.size > 0) @@ -72,16 +116,24 @@ class RestSwaggerApiDeclarationServlet extends ApiDeclarationServlet { val references = (for (listing <- listings.getOrElse(List())) yield { ApiListingReference(listing.resourcePath, listing.description) }).toList - val resourceListing = ResourceListing(config.apiVersion, - config.swaggerVersion, - references + val resourceListing = ResourceListing( + swaggerConfig.apiVersion, + swaggerConfig.swaggerVersion, + references, + List(), + swaggerConfig.info ) LOG.info("renderResourceListing write response -> {}", resourceListing) response.getOutputStream.write(JsonSerializer.asJson(resourceListing).getBytes("utf-8")) + } else { + response.setStatus(204) } } - override def renderApiDeclaration(request: HttpServletRequest, response: HttpServletResponse) = { + /** + * Renders the api listing of a single resource + */ + def renderApiDeclaration(request: HttpServletRequest, response: HttpServletResponse) = { val route = request.getPathInfo val docRoot = request.getPathInfo val f = new SpecFilter @@ -90,20 +142,9 @@ class RestSwaggerApiDeclarationServlet extends ApiDeclarationServlet { val headers = Map[String, List[String]]() val pathPart = docRoot - val camel = spring.getBean(classOf[CamelContext]) LOG.info("renderApiDeclaration camel -> {}", camel) if (camel != null) { - - val config = ConfigFactory.config - val rests = camel.getRestDefinitions - LOG.info("renderApiDeclaration rests -> {}", rests) - val list = reader.read(rests.get(0), config) - LOG.info("renderApiDeclaration reader -> {}", list) - val cache = Some(list.map(m => (m.resourcePath, m)).toMap) - LOG.info("renderApiDeclaration reader -> {}", cache) - - // val listings = ApiListingCache.listing(docRoot).map(specs => { - val listings = cache.map(specs => { + val listings = RestApiListingCache.listing(camel, swaggerConfig).map(specs => { (for (spec <- specs.values) yield { f.filter(spec, FilterFactory.filter, queryParams, cookies, headers) }).filter(m => m.resourcePath == pathPart) @@ -115,8 +156,10 @@ class RestSwaggerApiDeclarationServlet extends ApiDeclarationServlet { } case _ => response.setStatus(404) } + } else { + // no data + response.setStatus(204) } - response.setStatus(404) } } http://git-wip-us.apache.org/repos/asf/camel/blob/b234476a/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestSwaggerReader.scala ---------------------------------------------------------------------- diff --git a/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestSwaggerReader.scala b/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestSwaggerReader.scala index 2b57013..39f0f56 100644 --- a/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestSwaggerReader.scala +++ b/components/camel-swagger/src/main/scala/org/apache/camel/component/swagger/RestSwaggerReader.scala @@ -6,7 +6,7 @@ * (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 + * 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, @@ -48,6 +48,8 @@ class RestSwaggerReader { } } + // TOOD: register classloader + def read(rest: RestDefinition, config: SwaggerConfig): Option[ApiListing] = { val resourcePath = rest.getPath @@ -87,20 +89,12 @@ class RestSwaggerReader { case _ => "java.lang.Void" } - var p = verb.getProduces - if (p == null) { - p = rest.getProduces - } - val produces = p match { + val produces = verb.getProduces match { case e: String if e != "" => e.split(",").map(_.trim).toList case _ => List() } - var c = verb.getConsumes - if (c == null) { - c = rest.getConsumes - } - val consumes = c match { + val consumes = verb.getConsumes match { case e: String if e != "" => e.split(",").map(_.trim).toList case _ => List() } @@ -131,6 +125,16 @@ class RestSwaggerReader { if (apis.size > 0) { + val produces = rest.getProduces match { + case e: String if e != "" => e.split(",").map(_.trim).toList + case _ => List() + } + + val consumes = rest.getConsumes match { + case e: String if e != "" => e.split(",").map(_.trim).toList + case _ => List() + } + val models = ModelUtil.modelsFromApis(apis.toList) Some( ApiListing( @@ -138,8 +142,8 @@ class RestSwaggerReader { SwaggerSpec.version, config.basePath, resourcePath, - List(), // produces - List(), // consumes + produces, + consumes, List(), // protocols List(), // authorizations ModelUtil.stripPackages(apis.toList), http://git-wip-us.apache.org/repos/asf/camel/blob/b234476a/examples/camel-example-servlet-rest-tomcat/src/main/webapp/WEB-INF/web.xml ---------------------------------------------------------------------- diff --git a/examples/camel-example-servlet-rest-tomcat/src/main/webapp/WEB-INF/web.xml b/examples/camel-example-servlet-rest-tomcat/src/main/webapp/WEB-INF/web.xml index 0833518..e0c8151 100755 --- a/examples/camel-example-servlet-rest-tomcat/src/main/webapp/WEB-INF/web.xml +++ b/examples/camel-example-servlet-rest-tomcat/src/main/webapp/WEB-INF/web.xml @@ -39,26 +39,6 @@ <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> - - <!-- swagger servlet reader --> - <servlet> - <servlet-name>DefaultServletReaderConfig</servlet-name> - <servlet-class>com.wordnik.swagger.servlet.config.DefaultServletReaderConfig</servlet-class> - <load-on-startup>1</load-on-startup> - <init-param> - <param-name>swagger.resource.package</param-name> - <param-value>org.apache.camel.example.rest</param-value> - </init-param> - <init-param> - <param-name>swagger.api.basepath</param-name> - <param-value>http://localhost:8082</param-value> - </init-param> - <init-param> - <param-name>api.version</param-name> - <param-value>1.0.1</param-value> - </init-param> - </servlet> - <!-- to setup Camel Servlet --> <servlet> <servlet-name>CamelServlet</servlet-name> @@ -67,11 +47,23 @@ <load-on-startup>3</load-on-startup> </servlet> + <!-- to setup Camel Swagger api servlet --> <servlet> <servlet-name>ApiDeclarationServlet</servlet-name> - <!--<servlet-class>com.wordnik.swagger.servlet.listing.ApiDeclarationServlet</servlet-class>--> <servlet-class>org.apache.camel.component.swagger.RestSwaggerApiDeclarationServlet</servlet-class> <load-on-startup>2</load-on-startup> + <init-param> + <param-name>swagger.api.basepath</param-name> + <param-value>http://localhost:8080/api-docs</param-value> + </init-param> + <init-param> + <param-name>api.version</param-name> + <param-value>1.2.3</param-value> + </init-param> + <init-param> + <param-name>api.title</param-name> + <param-value>Camel Rest Example with Swagger</param-value> + </init-param> </servlet> <!-- define that url path for the Camel Servlet to use -->