blob: 30e1876ae92ba7ef3f8cb6cfd7dd17f4f253ce37 [file] [log] [blame]
/**
* 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 java.util.Locale
import com.wordnik.swagger.config.SwaggerConfig
import com.wordnik.swagger.model._
import com.wordnik.swagger.core.util.ModelUtil
import com.wordnik.swagger.core.SwaggerSpec
import org.apache.camel.model.rest.{RestOperationResponseMsgDefinition, RestOperationParamDefinition, VerbDefinition, RestDefinition}
import org.apache.camel.util.FileUtil
import org.slf4j.LoggerFactory
import scala.collection.mutable.ListBuffer
import com.wordnik.swagger.model.Parameter
import com.wordnik.swagger.model.ApiDescription
import com.wordnik.swagger.model.Operation
import com.wordnik.swagger.model.ApiListing
// to iterate Java list using for loop
import scala.collection.JavaConverters._
/**
* A Swagger reader that reads the Camel's Rest models and builds a Swagger ApiListing models.
*/
class RestSwaggerReader {
private val LOG = LoggerFactory.getLogger(classOf[RestSwaggerReader])
def read(rest: RestDefinition, config: SwaggerConfig): Option[ApiListing] = {
var resourcePath = rest.getPath
// resource path must start with slash
if (resourcePath == null) {
resourcePath = ""
}
if (!resourcePath.startsWith("/")) {
resourcePath = "/" + resourcePath
}
LOG.debug("Reading rest path: " + resourcePath + " -> " + rest)
// create a list of apis
val apis = new ListBuffer[ApiDescription]
// used during gathering of apis
val operations = new ListBuffer[Operation]
var path: String = null
var list = rest.getVerbs.asScala
// must sort the verbs by uri so we group them together when an uri has multiple operations
list = list.sorted(VerbOrdering)
for (verb: VerbDefinition <- list) {
if (verb.getUri != path && operations.size > 0) {
// restart
apis += ApiDescription(
buildUrl(resourcePath, path),
Some(""),
operations.toList)
operations.clear()
}
path = verb.getUri
// the method must be in upper case
var method = verb.asVerb().toUpperCase(Locale.US)
// create an unique nickname using the method and paths
var nickName = createNickname(verb.asVerb(), buildUrl(resourcePath, path))
var responseType = verb.getOutType match {
case e: String if e.endsWith("[]") => "List[" + e.substring(0, e.length - 2) + "]"
case e: String => e
case _ => "java.lang.Void"
}
val produces = verb.getProduces match {
case e: String if e != "" => e.split(",").map(_.trim).toList
case _ => List()
}
val consumes = verb.getConsumes match {
case e: String if e != "" => e.split(",").map(_.trim).toList
case _ => List()
}
var summary = verb.getDescriptionText
if (summary == null) {
summary = ""
}
LOG.debug("Adding operation " + method + " " + nickName)
operations += Operation(
method,
summary,
"",
responseType,
nickName,
0,
produces,
consumes,
List(),
List(),
createParameters(verb),
createResponseMessages(verb),
None)
}
// add remainder
if (operations.size > 0) {
apis += ApiDescription(
buildUrl(resourcePath, path),
Some(""),
operations.toList)
}
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)
LOG.debug("Adding APIs with {} models", models.size)
var desc = rest.getDescriptionText
if (desc == null) {
desc = ""
}
Some(
ApiListing(
config.apiVersion,
SwaggerSpec.version,
config.basePath,
resourcePath,
produces,
consumes,
List(), // protocols
List(), // authorizations
ModelUtil.stripPackages(apis.toList),
models,
Option(desc))
)
}
else None
}
def createResponseMessages(verb: VerbDefinition): List[ResponseMessage] = {
val responseMsgs = new ListBuffer[ResponseMessage]
for (param:RestOperationResponseMsgDefinition <- verb.getResponseMsgs.asScala) {
responseMsgs += ResponseMessage(
param.getCode.asInstanceOf[Integer],
param.getMessage,
Option( param.getResponseModel )
)
}
responseMsgs.toList
}
def createParameters(verb: VerbDefinition): List[Parameter] = {
val parameters = new ListBuffer[Parameter]
for (param:RestOperationParamDefinition <- verb.getParams.asScala) {
var allowValues=AnyAllowableValues
if(!param.getAllowableValues.isEmpty){
AllowableListValues(param.getAllowableValues.asScala.toList)
}
parameters += Parameter(
param.getName,
Some( param.getDescription ),
Some( param.getDefaultValue),
if (param.getRequired != null) param.getRequired.booleanValue() else false,
false,
param.getDataType,
allowValues,
param.getType.toString,
Some(param.getAccess)
)
}
parameters.toList
}
def createNickname(method: String, absPath : String): String = {
val s = method + "/" + absPath
val arr = s.split("\\/")
val r = arr.foldLeft("") {
(a, b) => a + toTitleCase(sanitizeNickname(b))
}
// first char should be lower
r.charAt(0).toLower + r.substring(1)
}
def toTitleCase(s: String): String = {
if (s.size > 0) {
s.charAt(0).toUpper + s.substring(1)
} else {
s
}
}
def sanitizeNickname(s: String): String = {
// nick name must only be alpha chars
s.replaceAll("\\W", "")
}
def buildUrl(path1: String, path2: String): String = {
val s1 = FileUtil.stripTrailingSeparator(path1)
val s2 = FileUtil.stripLeadingSeparator(path2)
if (s1 != null && s2 != null) {
s1 + "/" + s2
} else if (path1 != null) {
path1
} else {
path2
}
}
/**
* To sort the rest operations
*/
object VerbOrdering extends Ordering[VerbDefinition] {
def compare(a:VerbDefinition, b:VerbDefinition) = {
var u1 = ""
if (a.getUri != null) {
// replace { with _ which comes before a when soring by char
u1 = a.getUri.replace("{", "_")
}
var u2 = ""
if (b.getUri != null) {
// replace { with _ which comes before a when soring by char
u2 = b.getUri.replace("{", "_")
}
var num = u1.compareTo(u2)
if (num == 0) {
// same uri, so use http method as sorting
num = a.asVerb().compareTo(b.asVerb())
}
num
}
}
}