| /** |
| * 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 |
| } |
| } |
| |
| } |