Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

Expand All @@ -20,6 +21,7 @@ public class ScalaClientCodegen extends AbstractScalaCodegen implements CodegenC
protected String groupId = "io.swagger";
protected String artifactId = "swagger-scala-client";
protected String artifactVersion = "1.0.0";
protected String clientName = "AsyncClient";

public ScalaClientCodegen() {
super();
Expand Down Expand Up @@ -51,10 +53,13 @@ public ScalaClientCodegen() {
additionalProperties.put("asyncHttpClient", asyncHttpClient);
additionalProperties.put("authScheme", authScheme);
additionalProperties.put("authPreemptive", authPreemptive);
additionalProperties.put("clientName", clientName);

supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml"));
supportingFiles.add(new SupportingFile("apiInvoker.mustache",
(sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator), "ApiInvoker.scala"));
supportingFiles.add(new SupportingFile("client.mustache",
(sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator), clientName + ".scala"));
supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh"));
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
// gradle settings
Expand All @@ -75,8 +80,8 @@ public ScalaClientCodegen() {
importMapping.remove("Set");
importMapping.remove("Map");

importMapping.put("DateTime", "org.joda.time.DateTime");
importMapping.put("ListBuffer", "scala.collection.mutable.ListBuffer");
importMapping.put("Date", "java.util.Date");
importMapping.put("ListBuffer", "scala.collections.mutable.ListBuffer");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was probably missed during a merge conflict. It should be scala.collection.mutable.ListBuffer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codeniko thanks for reviewing.

@clasnake can you please take a look when you've time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem. I'll change the underlying library to akka-http and try to consolidate all three Scala clients into one(In a separate PR later). In fact that's the one I use in my daily work.


typeMapping = new HashMap<String, String>();
typeMapping.put("enum", "NSString");
Expand All @@ -90,14 +95,17 @@ public ScalaClientCodegen() {
typeMapping.put("byte", "Byte");
typeMapping.put("short", "Short");
typeMapping.put("char", "Char");
typeMapping.put("long", "Long");
typeMapping.put("double", "Double");
typeMapping.put("object", "Any");
typeMapping.put("file", "File");
//TODO binary should be mapped to byte array
// mapped to String as a workaround
typeMapping.put("binary", "String");
typeMapping.put("ByteArray", "String");
typeMapping.put("date-time", "Date");
// typeMapping.put("date", "Date");
// typeMapping.put("Date", "Date");
typeMapping.put("DateTime", "Date");

instantiationTypes.put("array", "ListBuffer");
instantiationTypes.put("map", "HashMap");
Expand All @@ -108,7 +116,6 @@ public ScalaClientCodegen() {
@Override
public void processOpts() {
super.processOpts();

if (additionalProperties.containsKey(CodegenConstants.MODEL_PROPERTY_NAMING)) {
setModelPropertyNaming((String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ class {{clientName}}(config: SwaggerConfig) extends Closeable {
client.close()
}
}

163 changes: 89 additions & 74 deletions modules/swagger-codegen/src/main/resources/scala/api.mustache
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{{>licenseInfo}}
package {{package}}

import java.text.SimpleDateFormat

{{#imports}}import {{import}}
{{/imports}}
import {{invokerPackage}}.ApiInvoker
import {{invokerPackage}}.ApiException
import {{invokerPackage}}.{ApiInvoker, ApiException}

import com.sun.jersey.multipart.FormDataMultiPart
import com.sun.jersey.multipart.file.FileDataBodyPart
Expand All @@ -16,13 +17,42 @@ import java.util.Date

import scala.collection.mutable.HashMap

import com.wordnik.swagger.client._
import scala.concurrent.Future
import collection.mutable

import java.net.URI

import com.wordnik.swagger.client.ClientResponseReaders.Json4sFormatsReader._
import com.wordnik.swagger.client.RequestWriters.Json4sFormatsWriter._

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent._
import scala.concurrent.duration._
import scala.util.{Failure, Success, Try}

{{#operations}}
class {{classname}}(val defBasePath: String = "{{{basePath}}}",
defApiInvoker: ApiInvoker = ApiInvoker) {

implicit val formats = new org.json4s.DefaultFormats {
override def dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS+0000")
}
implicit val stringReader = ClientResponseReaders.StringReader
implicit val unitReader = ClientResponseReaders.UnitReader
implicit val jvalueReader = ClientResponseReaders.JValueReader
implicit val jsonReader = JsonFormatsReader
implicit val stringWriter = RequestWriters.StringWriter
implicit val jsonWriter = JsonFormatsWriter

var basePath = defBasePath
var apiInvoker = defApiInvoker

def addHeader(key: String, value: String) = apiInvoker.defaultHeaders += key -> value
def addHeader(key: String, value: String) = apiInvoker.defaultHeaders += key -> value

val config = SwaggerConfig.forUrl(new URI(defBasePath))
val client = new RestClient(config)
val helper = new {{classname}}AsyncHelper(client, config)

{{#operation}}
/**
Expand All @@ -32,97 +62,82 @@ class {{classname}}(val defBasePath: String = "{{{basePath}}}",
{{/allParams}} * @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}
*/
def {{operationId}}({{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{#defaultValue}} /* = {{{defaultValue}}}*/{{/defaultValue}}{{/required}}{{^required}}Option[{{dataType}}]{{#defaultValue}} /* = {{{defaultValue}}}*/{{/defaultValue}}{{^defaultValue}} = None{{/defaultValue}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}}: Option[{{returnType}}]{{/returnType}} = {
// create path and map variables
val path = "{{{path}}}".replaceAll("\\{format\\}", "json"){{#pathParams}}.replaceAll("\\{" + "{{baseName}}" + "\\}",apiInvoker.escape({{paramName}})){{/pathParams}}
val await = Try(Await.result({{operationId}}Async({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}), Duration.Inf))
await match {
case Success(i) => Some(await.get)
case Failure(t) => None
}
}

val contentTypes = List({{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}}{{^consumes}}"application/json"{{/consumes}})
val contentType = contentTypes(0)
/**
* {{summary}} asynchronously
* {{notes}}
{{#allParams}} * @param {{paramName}} {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}
{{/allParams}} * @return Future({{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}})
*/
def {{operationId}}Async({{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{#defaultValue}} /* = {{{defaultValue}}}*/{{/defaultValue}}{{/required}}{{^required}}Option[{{dataType}}]{{#defaultValue}} /* = {{{defaultValue}}}*/{{/defaultValue}}{{^defaultValue}} = None{{/defaultValue}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}}: Future[{{returnType}}]{{/returnType}} = {
helper.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
}

val queryParams = new HashMap[String, String]
val headerParams = new HashMap[String, String]
val formParams = new HashMap[String, String]

{{#allParams}}
{{#required}}
{{^isPrimitiveType}}
if ({{paramName}} == null) throw new Exception("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}")
{{/operation}}
}

{{/isPrimitiveType}}
{{#isString}}
if ({{paramName}} == null) throw new Exception("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}")
class {{classname}}AsyncHelper(client: TransportClient, config: SwaggerConfig) extends ApiClient(client, config) {

{{/isString}}
{{/required}}
{{/allParams}}
{{#queryParams}}
{{#required}}
queryParams += "{{baseName}}" -> {{paramName}}.toString
{{/required}}
{{^required}}
{{paramName}}.map(paramVal => queryParams += "{{baseName}}" -> paramVal.toString)
{{/required}}
{{/queryParams}}

{{#headerParams}}
{{#required}}
headerParams += "{{baseName}}" -> {{paramName}}
{{/required}}
{{^required}}
{{paramName}}.map(paramVal => headerParams += "{{baseName}}" -> paramVal)
{{/required}}
{{/headerParams}}
{{#operation}}
def {{operationId}}({{#allParams}}{{^required}}{{paramName}}: Option[{{dataType}}] = {{#defaultValue}}Some({{defaultValue}}){{/defaultValue}}{{^defaultValue}}None{{/defaultValue}}{{#hasMore}},{{/hasMore}}
{{/required}}{{#required}}{{paramName}}: {{dataType}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{#hasMore}},
{{/hasMore}}{{/required}}{{/allParams}})(implicit reader: ClientResponseReader[{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}]{{#bodyParams}}, writer: RequestWriter[{{dataType}}]{{/bodyParams}}){{#returnType}}: Future[{{returnType}}]{{/returnType}}{{^returnType}}: Future[Unit]{{/returnType}} = {
// create path and map variables
val path = (addFmt("{{path}}"){{#pathParams}}
replaceAll ("\\{" + "{{baseName}}" + "\\}",{{paramName}}.toString){{/pathParams}})

var postBody: AnyRef = {{#bodyParam}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}.map(paramVal => paramVal){{/required}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}
// query params
val queryParams = new mutable.HashMap[String, String]
val headerParams = new mutable.HashMap[String, String]

if (contentType.startsWith("multipart/form-data")) {
val mp = new FormDataMultiPart
{{#formParams}}
{{#notFile}}
{{#allParams}}
{{#required}}
mp.field("{{baseName}}", {{paramName}}.toString, MediaType.MULTIPART_FORM_DATA_TYPE)
{{^isPrimitiveType}}
if ({{paramName}} == null) throw new Exception("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}")
{{/isPrimitiveType}}
{{#isString}}
if ({{paramName}} == null) throw new Exception("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}")

{{/isString}}
{{/required}}
{{/allParams}}
{{#queryParams}}
{{^required}}
{{paramName}}.map(paramVal => mp.field("{{baseName}}", paramVal.toString, MediaType.MULTIPART_FORM_DATA_TYPE))
{{paramName}} match {
case Some(param) => queryParams += "{{baseName}}" -> param.toString
case _ => queryParams
}
{{/required}}
{{/notFile}}
{{#isFile}}
{{#required}}
mp.field("{{baseName}}", file.getName)
mp.bodyPart(new FileDataBodyPart("{{baseName}}", {{paramName}}, MediaType.MULTIPART_FORM_DATA_TYPE))
queryParams += "{{baseName}}" -> {{paramName}}.toString
{{/required}}
{{/queryParams}}
{{#headerParams}}
{{^required}}
file.map(fileVal => mp.field("{{baseName}}", fileVal.getName))
{{paramName}}.map(paramVal => mp.bodyPart(new FileDataBodyPart("{{baseName}}", paramVal, MediaType.MULTIPART_FORM_DATA_TYPE)))
{{paramName}} match {
case Some(param) => headerParams += "{{baseName}}" -> param.toString
case _ => headerParams
}
{{/required}}
{{/isFile}}
{{/formParams}}
postBody = mp
} else {
{{#formParams}}
{{#notFile}}
{{#required}}
formParams += "{{baseName}}" -> {{paramName}}.toString
headerParams += "{{baseName}}" -> {{paramName}}.toString
{{/required}}
{{^required}}
{{paramName}}.map(paramVal => formParams += "{{baseName}}" -> paramVal.toString)
{{/required}}
{{/notFile}}
{{/formParams}}
}
{{/headerParams}}

try {
apiInvoker.invokeApi(basePath, path, "{{httpMethod}}", queryParams.toMap, formParams.toMap, postBody, headerParams.toMap, contentType) match {
case s: String =>
{{#returnType}} Some(apiInvoker.deserialize(s, "{{returnContainer}}", classOf[{{returnBaseType}}]).asInstanceOf[{{returnType}}])
{{/returnType}}
case _ => None
}
} catch {
case ex: ApiException if ex.code == 404 => None
case ex: ApiException => throw ex
val resFuture = client.submit("{{httpMethod}}", path, queryParams.toMap, headerParams.toMap, {{#bodyParam}}writer.write({{paramName}}){{/bodyParam}}{{^bodyParam}}"{{emptyBodyParam}}"{{/bodyParam}})
resFuture flatMap { resp =>
process(reader.read(resp))
}
}

{{/operation}}
{{/operation}}

}
{{/operations}}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ ext {
jackson_version = "2.4.2"
junit_version = "4.8.1"
scala_test_version = "2.2.4"
swagger_async_httpclient_version = "0.3.5"
}

repositories {
mavenLocal()
mavenCentral()
}

dependencies {
Expand All @@ -117,4 +123,5 @@ dependencies {
testCompile "junit:junit:$junit_version"
compile "joda-time:joda-time:$jodatime_version"
compile "org.joda:joda-convert:$joda_version"
compile "com.wordnik.swagger:swagger-async-httpclient_2.10:$swagger_async_httpclient_version"
}
67 changes: 34 additions & 33 deletions modules/swagger-codegen/src/main/resources/scala/build.sbt.mustache
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
lazy val root = (project in file(".")).
settings(
version := "{{artifactVersion}}",
name := "{{artifactId}}",
organization := "{{groupId}}",
scalaVersion := "2.11.8",

libraryDependencies ++= Seq(
"com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.4.2",
"com.sun.jersey" % "jersey-core" % "1.19",
"com.sun.jersey" % "jersey-client" % "1.19",
"com.sun.jersey.contribs" % "jersey-multipart" % "1.19",
"org.jfarcand" % "jersey-ahc-client" % "1.0.5",
"io.swagger" % "swagger-core" % "1.5.8",
"joda-time" % "joda-time" % "2.2",
"org.joda" % "joda-convert" % "1.2",
"org.scalatest" %% "scalatest" % "2.2.4" % "test",
"junit" % "junit" % "4.8.1" % "test"
),

resolvers ++= Seq(
Resolver.jcenterRepo,
Resolver.mavenLocal
),

scalacOptions := Seq(
"-unchecked",
"-deprecation",
"-feature"
),

publishArtifact in (Compile, packageDoc) := false
)
version := "{{artifactVersion}}"

name := "{{artifactId}}"

organization := "{{groupId}}"

scalaVersion := "2.11.8"

libraryDependencies ++= Seq(
"com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.4.2",
"com.sun.jersey" % "jersey-core" % "1.19",
"com.sun.jersey" % "jersey-client" % "1.19",
"com.sun.jersey.contribs" % "jersey-multipart" % "1.19",
"org.jfarcand" % "jersey-ahc-client" % "1.0.5",
"io.swagger" % "swagger-core" % "1.5.8",
"joda-time" % "joda-time" % "2.2",
"org.joda" % "joda-convert" % "1.2",
"org.scalatest" %% "scalatest" % "2.2.4" % "test",
"junit" % "junit" % "4.8.1" % "test",
"com.wordnik.swagger" %% "swagger-async-httpclient" % "0.3.5"
)

resolvers ++= Seq(
Resolver.mavenLocal
)

scalacOptions := Seq(
"-unchecked",
"-deprecation",
"-feature"
)

publishArtifact in (Compile, packageDoc) := false

22 changes: 22 additions & 0 deletions modules/swagger-codegen/src/main/resources/scala/client.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package {{invokerPackage}}

{{#imports}}import {{import}}
{{/imports}}
import {{apiPackage}}._

import com.wordnik.swagger.client._

import java.io.Closeable

class {{clientName}}(config: SwaggerConfig) extends Closeable {
val locator = config.locator
val name = config.name

private[this] val client = transportClient

protected def transportClient: TransportClient = new RestClient(config)

def close() {
client.close()
}
}
Loading