allow adding typeMappings and includes through config

This commit is contained in:
Ivan Porto Carrero 2013-06-23 18:35:48 -07:00
parent c1e018947d
commit dc3a4b4eb5
4 changed files with 138 additions and 87 deletions

View File

@ -5,7 +5,7 @@ organization := "com.wordnik"
name := "swagger-codegen" name := "swagger-codegen"
version := "2.0.3" version := "2.0.5"
scalaVersion := "2.9.2" scalaVersion := "2.9.2"
@ -89,7 +89,7 @@ pomExtra <<= (pomExtra, name, description) {(pom, name, desc) => pom ++ Group(
</developer> </developer>
<developer> <developer>
<id>ayush</id> <id>ayush</id>
<name>Ayush Gupts</name> <name>Ayush Gupta</name>
<email>ayush@glugbot.com</email> <email>ayush@glugbot.com</email>
</developer> </developer>
<developer> <developer>

View File

@ -16,7 +16,7 @@ class {{classname}}(client: TransportClient, config: SwaggerConfig) extends ApiC
val path = val path =
("{{path}}" ("{{path}}"
replaceAll ("\\{format\\}", ser.name) replaceAll ("\\{format\\}", ser.name)
{{#pathParams}}replaceAll ("\\{" + "{{baseName}}" + "\\}",{{paramName}}) {{#pathParams}}replaceAll ("\\{" + "{{baseName}}" + "\\}",{{paramName}}.toString)
{{/pathParams}}) {{/pathParams}})
// query params // query params
@ -25,7 +25,7 @@ class {{classname}}(client: TransportClient, config: SwaggerConfig) extends ApiC
{{#requiredParamCount}} {{#requiredParamCount}}
// verify required params are set // verify required params are set
val paramCount = (Set({{/requiredParamCount}}{{#requiredParams}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/requiredParams}}{{#requiredParamCount}}) - null).size val paramCount = (Set[Any]({{/requiredParamCount}}{{#requiredParams}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/requiredParams}}{{#requiredParamCount}}) - null).size
if (paramCount != {{requiredParamCount}}) sys.error("missing required params") if (paramCount != {{requiredParamCount}}) sys.error("missing required params")
{{/requiredParamCount}} {{/requiredParamCount}}

View File

@ -38,7 +38,7 @@ import scala.collection.mutable.{ HashMap, ListBuffer, HashSet }
import scala.collection.JavaConversions._ import scala.collection.JavaConversions._
object Codegen { object Codegen {
val templates = new HashMap[String, (TemplateEngine, Template)] val templates = new HashMap[String, (String, (TemplateEngine, Template))]
} }
class Codegen(config: CodegenConfig) { class Codegen(config: CodegenConfig) {
@ -136,29 +136,7 @@ class Codegen(config: CodegenConfig) {
}) })
val rootDir = new java.io.File(".") val rootDir = new java.io.File(".")
val engineData = Codegen.templates.getOrElse(templateFile, { val (resourcePath, (engine, template)) = Codegen.templates.getOrElseUpdate(templateFile, compileTemplate(templateFile, Some(rootDir)))
val engine = new TemplateEngine(Some(rootDir))
val srcName = config.templateDir + File.separator + templateFile
val srcStream = {
getClass.getClassLoader.getResourceAsStream(srcName) match {
case is: java.io.InputStream => is
case _ => {
val f = new java.io.File(srcName)
if (!f.exists) throw new Exception("Missing template: " + srcName)
else new java.io.FileInputStream(f)
}
}
}
val template = engine.compile(
TemplateSource.fromText(config.templateDir + File.separator + templateFile,
Source.fromInputStream(srcStream).mkString))
val t = Tuple2(engine, template)
Codegen.templates += templateFile -> t
t
})
val engine = engineData._1
val template = engineData._2
val requiredModels = { val requiredModels = {
for(i <- allImports) yield { for(i <- allImports) yield {
@ -182,13 +160,33 @@ class Codegen(config: CodegenConfig) {
"operations" -> f, "operations" -> f,
"models" -> modelData, "models" -> modelData,
"basePath" -> bundle.getOrElse("basePath", "")) "basePath" -> bundle.getOrElse("basePath", ""))
var output = engine.layout(config.templateDir + File.separator + templateFile, template, data.toMap) var output = engine.layout(resourcePath, template, data.toMap)
// a shutdown method will be added to scalate in an upcoming release // a shutdown method will be added to scalate in an upcoming release
engine.compiler.shutdown engine.compiler.shutdown
output output
} }
protected def compileTemplate(templateFile: String, rootDir: Option[File] = None, engine: Option[TemplateEngine] = None): (String, (TemplateEngine, Template)) = {
val engine = new TemplateEngine(rootDir orElse Some(new File(".")))
val srcName = config.templateDir + File.separator + templateFile
val srcStream = {
getClass.getClassLoader.getResourceAsStream(srcName) match {
case is: java.io.InputStream => is
case _ => {
val f = new java.io.File(srcName)
if (!f.exists) throw new Exception("Missing template: " + srcName)
else new java.io.FileInputStream(f)
}
}
}
val template = engine.compile(
TemplateSource.fromText(config.templateDir + File.separator + templateFile,
Source.fromInputStream(srcStream).mkString))
(srcName, engine -> template)
}
def allowableValuesToString(v: AllowableValues) = { def allowableValuesToString(v: AllowableValues) = {
v match { v match {
case av: AllowableListValues => { case av: AllowableListValues => {
@ -532,13 +530,7 @@ class Codegen(config: CodegenConfig) {
if (supportingFile.endsWith(".mustache")) { if (supportingFile.endsWith(".mustache")) {
val output = { val output = {
val resourceName = config.templateDir + File.separator + supportingFile val (resourceName, (_, template)) = compileTemplate(supportingFile, Some(rootDir), Some(engine))
val is = getInputStream(resourceName)
if (is == null)
throw new Exception("Resource not found: " + resourceName)
val template = engine.compile(
TemplateSource.fromText(resourceName,
Source.fromInputStream(is).mkString))
engine.layout(resourceName, template, data.toMap) engine.layout(resourceName, template, data.toMap)
} }
val fw = new FileWriter(outputFilename, false) val fw = new FileWriter(outputFilename, false)

View File

@ -7,7 +7,7 @@ import scala.collection.mutable.{HashMap, ListBuffer}
import language.CodegenConfig import language.CodegenConfig
import scala.io.Source import scala.io.Source
import org.json4s.jackson.Serialization._ import org.json4s.jackson.Serialization._
import org.fusesource.scalate.{TemplateSource, TemplateEngine} import org.fusesource.scalate.{Template, TemplateSource, TemplateEngine}
import org.apache.commons.io.FileUtils import org.apache.commons.io.FileUtils
import com.wordnik.swagger.codegen.util.{CoreUtils, ApiExtractor, ResourceExtractor} import com.wordnik.swagger.codegen.util.{CoreUtils, ApiExtractor, ResourceExtractor}
import com.wordnik.swagger.codegen.spec.SwaggerSpecValidator import com.wordnik.swagger.codegen.spec.SwaggerSpecValidator
@ -20,18 +20,46 @@ case class SwaggerApi(
packageName: String, packageName: String,
apiTemplates: Map[String, String] = Map("api.mustache" -> ".scala"), apiTemplates: Map[String, String] = Map("api.mustache" -> ".scala"),
modelTemplates: Map[String, String] = Map("model.mustache" -> ".scala"), modelTemplates: Map[String, String] = Map("model.mustache" -> ".scala"),
apiKey: Option[String] = None) apiKey: Option[String] = None,
baseUrl: Option[String] = None,
excludedApis: Set[String] = Set.empty)
case class SwaggerGenConfig( case class SwaggerGenConfig(
api: SwaggerApi, api: SwaggerApi,
templateDir: File, templateDir: File,
codeDir: File, codeDir: File,
projectRoot: File) projectRoot: File,
defaultIncludes: Set[String] = Set.empty,
typeMapping: Map[String, String] = Map.empty,
defaultImports: Map[String, String] = Map.empty)
object AsycnClientGeneratorConf {
val appBanner: String = """
|
|
| .--.--.
| / / '.
|| : /`. / .---. __ ,-.
|; | |--` /. ./| ,----._,. ,----._,. ,' ,'/ /|
|| : ;_ .-'-. ' | ,--.--. / / ' // / ' / ,---. ' | |' |
| \ \ `. /___/ \: |/ \| : | : | / \| | ,'
| `----. \.-'.. ' ' .--. .-. | | .\ | | .\ ./ / ' : /
| __ \ \ /___/ \: '\__\/: . . ; '; . ; '; . ' / | | '
| / /`--' . \ ' .\ ," .--.; ' . . ' . . ' ; /; : |
|'--'. / \ \ ' \ / / ,. |`---`-'| |`---`-'| ' | / | , ;
| `--'---' \ \ |--; : .' .'__/\_: |.'__/\_: | : |---'
| \ \ | | , .-.| : :| : :\ \ /
| '---" `--`---' \ \ / \ \ / `----'
| `--`-' `--`-'
|
| Swagger Codegen, Reverb Technologies Inc. (c) 2009-2013
| For more info, visit: https://developers.helloreverb.com/swagger/
""".stripMargin
}
class AsycnClientGeneratorConf(arguments: Seq[String]) extends ScallopConf(arguments) { class AsycnClientGeneratorConf(arguments: Seq[String]) extends ScallopConf(arguments) {
val name = opt[String](required = true, descr = "The name of the generated client.") val name = opt[String](required = true, descr = "The name of the generated client.")
val `package` = opt[String](default = Some("com.wordnik.swagger.client.async"), descr = "The package for the generated code.") val `package` = opt[String](default = Some("com.wordnik.swagger.client.async"), descr = "The package for the generated code.")
val resourceUrl = trailArg[String](descr = "The url to use for fetching the swagger spec from. This can be a http(s) url or a file path.") val resourceUrl = trailArg[String](descr = "The url to use for fetching the swagger spec from. This can be a http(s) url or a file path.")
val baseUrl = opt[String](descr = "The url to use when you want to override the base url provided by the resource url json.")
val apiKey = opt[String](required = false, descr = "An optional api key to use when calling the swagger api") val apiKey = opt[String](required = false, descr = "An optional api key to use when calling the swagger api")
val templateDir = opt[String](descr = "The directory that contains the templates for use in this generator", default = Some("asyncscala")) val templateDir = opt[String](descr = "The directory that contains the templates for use in this generator", default = Some("asyncscala"))
val codeDir = opt[String](descr = "The directory to use as base for generating code files, this will contain the generated scala files.", default = Some("src/main/scala"), hidden = true) val codeDir = opt[String](descr = "The directory to use as base for generating code files, this will contain the generated scala files.", default = Some("src/main/scala"), hidden = true)
@ -53,27 +81,7 @@ class AsycnClientGeneratorConf(arguments: Seq[String]) extends ScallopConf(argum
} }
object ScalaAsyncClientGenerator extends App { object ScalaAsyncClientGenerator extends App {
val appBanner = """ val appBanner: String = AsycnClientGeneratorConf.appBanner
|
|
| .--.--.
| / / '.
|| : /`. / .---. __ ,-.
|; | |--` /. ./| ,----._,. ,----._,. ,' ,'/ /|
|| : ;_ .-'-. ' | ,--.--. / / ' // / ' / ,---. ' | |' |
| \ \ `. /___/ \: |/ \| : | : | / \| | ,'
| `----. \.-'.. ' ' .--. .-. | | .\ | | .\ ./ / ' : /
| __ \ \ /___/ \: '\__\/: . . ; '; . ; '; . ' / | | '
| / /`--' . \ ' .\ ," .--.; ' . . ' . . ' ; /; : |
|'--'. / \ \ ' \ / / ,. |`---`-'| |`---`-'| ' | / | , ;
| `--'---' \ \ |--; : .' .'__/\_: |.'__/\_: | : |---'
| \ \ | | , .-.| : :| : :\ \ /
| '---" `--`---' \ \ / \ \ / `----'
| `--`-' `--`-'
|
| Swagger Codegen, Reverb Technologies Inc. (c) 2009-2013
| For more info, visit: https://developers.helloreverb.com/swagger/
""".stripMargin
val opts = new AsycnClientGeneratorConf(if (args.nonEmpty) args else Array("--help")) val opts = new AsycnClientGeneratorConf(if (args.nonEmpty) args else Array("--help"))
val rootDir = new File(opts.projectRoot()) val rootDir = new File(opts.projectRoot())
@ -87,8 +95,12 @@ object ScalaAsyncClientGenerator extends App {
if (!r.startsWith("http") && !r.startsWith("file")) sys.props("fileMap") = r if (!r.startsWith("http") && !r.startsWith("file")) sys.props("fileMap") = r
r r
} }
val baseUrl = {
val r = opts.baseUrl()
if (r == null || r.trim.isEmpty) None else Some(r)
}
val cfg = SwaggerGenConfig( val cfg = SwaggerGenConfig(
api = SwaggerApi(opts.name(), opts.resourceUrl(), opts.`package`(), apiKey = opts.apiKey.get), api = SwaggerApi(opts.name(), resUrl, opts.`package`(), apiKey = opts.apiKey.get, baseUrl = baseUrl),
templateDir = new File(opts.templateDir()), templateDir = new File(opts.templateDir()),
codeDir = new File(rootDir, opts.codeDir()), codeDir = new File(rootDir, opts.codeDir()),
projectRoot = rootDir projectRoot = rootDir
@ -156,13 +168,7 @@ class AsyncClientCodegen(clientName: String, config: CodegenConfig, rootDir: Opt
if (supportingFile.endsWith(".mustache")) { if (supportingFile.endsWith(".mustache")) {
val output = { val output = {
val resourceName = config.templateDir + File.separator + supportingFile val (resourceName, (_, template)) = compileTemplate(supportingFile, rootDir, Some(engine))
val is = getInputStream(resourceName)
if (is == null)
throw new Exception("Resource not found: " + resourceName)
val template = engine.compile(
TemplateSource.fromText(resourceName,
Source.fromInputStream(is).mkString))
engine.layout(resourceName, template, data.toMap) engine.layout(resourceName, template, data.toMap)
} }
val fw = new FileWriter(outputFilename, false) val fw = new FileWriter(outputFilename, false)
@ -192,6 +198,19 @@ class AsyncClientCodegen(clientName: String, config: CodegenConfig, rootDir: Opt
engine.compiler.shutdown() engine.compiler.shutdown()
} }
override protected def compileTemplate(templateFile: String, rootDir: Option[File] = None, engine: Option[TemplateEngine] = None): (String, (TemplateEngine, Template)) = {
val eng = engine getOrElse new TemplateEngine(rootDir orElse Some(new File(".")))
val rn = config.templateDir + File.separator + templateFile
val rrn = "asyncscala" + File.separator + templateFile
val resourceName = if (new File(rn).exists) rn else rrn
val is = getInputStream(resourceName)
if (is == null)
throw new Exception("Missing template: " + resourceName)
val template = eng.compile(TemplateSource.fromText(resourceName,Source.fromInputStream(is).mkString))
(resourceName, eng -> template)
}
} }
class ScalaAsyncClientGenerator(cfg: SwaggerGenConfig) extends BasicGenerator { class ScalaAsyncClientGenerator(cfg: SwaggerGenConfig) extends BasicGenerator {
@ -203,37 +222,73 @@ class ScalaAsyncClientGenerator(cfg: SwaggerGenConfig) extends BasicGenerator {
override val fileSuffix: String = ".scala" override val fileSuffix: String = ".scala"
override val modelPackage: Option[String] = Some(packageName + ".model") override val modelPackage: Option[String] = Some(packageName + ".model")
override val apiPackage: Option[String] = Some(packageName + ".apis") override val apiPackage: Option[String] = Some(packageName + ".apis")
override val reservedWords: Set[String] = Set("type", "package", "match", "object") override val reservedWords: Set[String] =
override val importMapping = Map("Date" -> "org.joda.time.DateTime", "DateTimeZone" -> "org.joda.time.DateTimeZone", "Chronology" -> "org.joda.time.Chronology") Set(
"abstract",
"case",
"catch",
"class",
"def",
"do",
"else",
"extends",
"false",
"final",
"finally",
"for",
"forSome",
"if",
"implicit",
"import",
"lazy",
"match",
"new",
"null",
"object",
"override",
"package",
"private",
"protected",
"return",
"sealed",
"super",
"this",
"throw",
"trait",
"try",
"true",
"type",
"val",
"var",
"while",
"with",
"yield")
override val importMapping = cfg.defaultImports
override val typeMapping = Map( override val typeMapping = Map(
"boolean" -> "Boolean", "boolean" -> "Boolean",
"string" -> "String", "string" -> "String",
"int" -> "Int", "int" -> "Int",
"float" -> "Float", "float" -> "Float",
"byte" -> "Byte",
"short" -> "Short",
"char" -> "Char",
"long" -> "Long", "long" -> "Long",
"double" -> "Double", "double" -> "Double",
"object" -> "Any", "object" -> "Any") ++ cfg.typeMapping
"Date" -> "DateTime",
"BCryptPassword" -> "String")
override val defaultIncludes = Set( override val defaultIncludes = Set(
"Int", "Int",
"int",
"String", "String",
"string",
"Long", "Long",
"long", "Short",
"Char",
"Byte",
"Float", "Float",
"float",
"Double", "Double",
"double",
"Boolean", "Boolean",
"boolean", "AnyRef",
"DateTime", "Any") ++ cfg.defaultIncludes
"DateTimeZone",
"Chronology",
"object",
"Any")
override def supportingFiles = List( override def supportingFiles = List(
("client.mustache", destinationDir + "/" + cfg.api.packageName.replace('.', '/'), (pascalizedClientName +".scala")), ("client.mustache", destinationDir + "/" + cfg.api.packageName.replace('.', '/'), (pascalizedClientName +".scala")),
("sbt.mustache", cfg.projectRoot.getPath, "swagger-client.sbt") ("sbt.mustache", cfg.projectRoot.getPath, "swagger-client.sbt")
@ -245,6 +300,10 @@ class ScalaAsyncClientGenerator(cfg: SwaggerGenConfig) extends BasicGenerator {
codegen = new AsyncClientCodegen(cfg.api.clientName, this, Some(cfg.projectRoot)) codegen = new AsyncClientCodegen(cfg.api.clientName, this, Some(cfg.projectRoot))
override def getBasePath(basePath: String): String =
cfg.api.baseUrl.getOrElse(super.getBasePath(basePath))
override def generateClient(args: Array[String]) = { override def generateClient(args: Array[String]) = {
val host = cfg.api.resourceUrl val host = cfg.api.resourceUrl
@ -344,7 +403,7 @@ class ScalaAsyncClientGenerator(cfg: SwaggerGenConfig) extends BasicGenerator {
*/ */
override def prepareModelMap(models: Map[String, Model]): List[Map[String, AnyRef]] = { override def prepareModelMap(models: Map[String, Model]): List[Map[String, AnyRef]] = {
(for ((name, schema) <- models) yield { (for ((name, schema) <- models) yield {
if (!defaultIncludes.contains(name) && !name.equalsIgnoreCase("subscribe")) { if (!defaultIncludes.contains(name)) {
Some(Map( Some(Map(
"name" -> toModelName(name), "name" -> toModelName(name),
"className" -> name, "className" -> name,
@ -408,7 +467,7 @@ class ScalaAsyncClientGenerator(cfg: SwaggerGenConfig) extends BasicGenerator {
val opMap = new mutable.HashMap[(String, String), mutable.ListBuffer[(String, Operation)]] val opMap = new mutable.HashMap[(String, String), mutable.ListBuffer[(String, Operation)]]
for ((basePath, apiPath, operation) <- operations) { for ((basePath, apiPath, operation) <- operations) {
val className = resourceNameFromFullPath(apiPath) val className = resourceNameFromFullPath(apiPath)
if (!className.equalsIgnoreCase("baldr")) { if (!cfg.api.excludedApis.exists(_.equalsIgnoreCase(className))) {
val listToAddTo = opMap.getOrElse((basePath, className), { val listToAddTo = opMap.getOrElse((basePath, className), {
val l = new mutable.ListBuffer[(String, Operation)] val l = new mutable.ListBuffer[(String, Operation)]
opMap += (basePath, className) -> l opMap += (basePath, className) -> l