Merge branch 'develop_2.0' into develop_2.0_objc_contenttype

Conflicts:
	samples/client/petstore/objc/client/SWGPetApi.m
This commit is contained in:
geekerzp 2015-05-22 17:32:57 +08:00
commit 87a730b5a2
313 changed files with 14060 additions and 6489 deletions

12
.gitignore vendored
View File

@ -22,7 +22,17 @@ samples/server-generator/scalatra/output
samples/server-generator/node/output/node_modules samples/server-generator/node/output/node_modules
samples/server-generator/scalatra/target samples/server-generator/scalatra/target
samples/server-generator/scalatra/output/.history samples/server-generator/scalatra/output/.history
samples/client/petstore/qt5cpp/PetStore/moc_*
samples/client/petstore/qt5cpp/PetStore/*.o
samples/client/petstore/objc/PetstoreClient.xcworkspace/xcuserdata
samples/client/petstore/qt5cpp/build-*
samples/client/petstore/qt5cpp/PetStore/PetStore
samples/client/petstore/qt5cpp/PetStore/Makefile
samples/client/petstore/java/hello.txt
samples/client/petstore/android-java/hello.txt
samples/client/petstore/objc/Build
samples/client/petstore/objc/Pods
samples/server/petstore/nodejs/node_modules
target target
.idea .idea
.lib .lib

View File

@ -150,8 +150,11 @@ JavaClientCodegen.java
JaxRSServerCodegen.java JaxRSServerCodegen.java
NodeJSServerCodegen.java NodeJSServerCodegen.java
ObjcClientCodegen.java ObjcClientCodegen.java
PerlClientCodegen.java
PhpClientCodegen.java PhpClientCodegen.java
Python3ClientCodegen.java
PythonClientCodegen.java PythonClientCodegen.java
Qt5CPPGenerator.java
RubyClientCodegen.java RubyClientCodegen.java
ScalaClientCodegen.java ScalaClientCodegen.java
ScalatraServerCodegen.java ScalatraServerCodegen.java

View File

@ -26,6 +26,6 @@ fi
# if you've executed sbt assembly previously it will use that instead. # if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="$@ generate -t modules/swagger-codegen/src/main/resources/JavaJaxRS -i modules/swagger-codegen/src/test/resources/2_0/petstore.json -l jaxrs -o samples/server/petstore/jaxrs" ags="$@ generate -t modules/swagger-codegen/src/main/resources/JavaJaxRS -i modules/swagger-codegen/src/test/resources/2_0/petstore.json -l jaxrs -o samples/server/petstore/jaxrs -Dswagger.codegen.jaxrs.impl.source=src/main/java"
java $JAVA_OPTS -jar $executable $ags java $JAVA_OPTS -jar $executable $ags

View File

@ -54,10 +54,16 @@ public class Generate implements Runnable {
"Pass in a URL-encoded string of name:header with a comma separating multiple values") "Pass in a URL-encoded string of name:header with a comma separating multiple values")
private String auth; private String auth;
@Option( name= {"-D"}, title = "system properties", description = "sets specified system properties in " +
"the format of name=value,name=value")
private String systemProperties;
@Override @Override
public void run() { public void run() {
verbosed(verbose); verbosed(verbose);
setSystemProperties();
ClientOptInput input = new ClientOptInput(); ClientOptInput input = new ClientOptInput();
if (isNotEmpty(auth)) { if (isNotEmpty(auth)) {
@ -77,6 +83,17 @@ public class Generate implements Runnable {
new DefaultGenerator().opts(input.opts(new ClientOpts()).swagger(swagger)).generate(); new DefaultGenerator().opts(input.opts(new ClientOpts()).swagger(swagger)).generate();
} }
private void setSystemProperties() {
if( systemProperties != null && systemProperties.length() > 0 ){
for( String property : systemProperties.split(",")) {
int ix = property.indexOf('=');
if( ix > 0 && ix < property.length()-1 ){
System.setProperty( property.substring(0, ix), property.substring(ix+1) );
}
}
}
}
/** /**
* If true parameter, adds system properties which enables debug mode in generator * If true parameter, adds system properties which enables debug mode in generator
* @param verbose - if true, enables debug mode * @param verbose - if true, enables debug mode

View File

@ -6,11 +6,9 @@
<relativePath>../..</relativePath> <relativePath>../..</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.wordnik</groupId>
<artifactId>swagger-codegen</artifactId> <artifactId>swagger-codegen</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>swagger-codegen (core library)</name> <name>swagger-codegen (core library)</name>
<version>2.1.1-M2-SNAPSHOT</version>
<build> <build>
<sourceDirectory>src/main/java</sourceDirectory> <sourceDirectory>src/main/java</sourceDirectory>
<defaultGoal>install</defaultGoal> <defaultGoal>install</defaultGoal>

View File

@ -56,4 +56,8 @@ public interface CodegenConfig {
Map<String, Object> postProcessModels(Map<String, Object> objs); Map<String, Object> postProcessModels(Map<String, Object> objs);
Map<String, Object> postProcessOperations(Map<String, Object> objs); Map<String, Object> postProcessOperations(Map<String, Object> objs);
Map<String, Object> postProcessSupportingFileData(Map<String, Object> objs); Map<String, Object> postProcessSupportingFileData(Map<String, Object> objs);
String apiFilename(String templateName, String tag);
boolean shouldOverwrite(String filename);
} }

View File

@ -11,6 +11,6 @@ public class CodegenModel {
public String defaultValue; public String defaultValue;
public List<CodegenProperty> vars = new ArrayList<CodegenProperty>(); public List<CodegenProperty> vars = new ArrayList<CodegenProperty>();
public Set<String> imports = new HashSet<String>(); public Set<String> imports = new HashSet<String>();
public Boolean hasVars, emptyVars, hasMoreModels; public Boolean hasVars, emptyVars, hasMoreModels, hasEnums;
public ExternalDocs externalDocs; public ExternalDocs externalDocs;
} }

View File

@ -3,7 +3,7 @@ package com.wordnik.swagger.codegen;
public class CodegenParameter { public class CodegenParameter {
public Boolean isFormParam, isQueryParam, isPathParam, isHeaderParam, public Boolean isFormParam, isQueryParam, isPathParam, isHeaderParam,
isCookieParam, isBodyParam, isFile, notFile, hasMore, isContainer, secondaryParam; isCookieParam, isBodyParam, isFile, notFile, hasMore, isContainer, secondaryParam;
public String baseName, paramName, dataType, collectionFormat, description, baseType; public String baseName, paramName, dataType, collectionFormat, description, baseType, defaultValue;
public String jsonSchema; public String jsonSchema;
/** /**
@ -34,7 +34,8 @@ public class CodegenParameter {
output.isBodyParam = this.isBodyParam; output.isBodyParam = this.isBodyParam;
output.required = this.required; output.required = this.required;
output.jsonSchema = this.jsonSchema; output.jsonSchema = this.jsonSchema;
output.defaultValue = this.defaultValue;
return output; return output;
} }
} }

View File

@ -16,7 +16,10 @@ public class CodegenProperty {
public String example; public String example;
public String jsonSchema; public String jsonSchema;
public Double minimum, maximum, exclusiveMinimum, exclusiveMaximum; public Double minimum;
public Double maximum;
public Boolean exclusiveMinimum;
public Boolean exclusiveMaximum;
public Boolean hasMore = null, required = null, secondaryParam = null; public Boolean hasMore = null, required = null, secondaryParam = null;
public Boolean isPrimitiveType, isContainer, isNotContainer; public Boolean isPrimitiveType, isContainer, isNotContainer;
public boolean isEnum; public boolean isEnum;

View File

@ -1,17 +1,21 @@
package com.wordnik.swagger.codegen; package com.wordnik.swagger.codegen;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class CodegenResponse { public class CodegenResponse {
public String code, message; public String code, message;
public Boolean hasMore; public Boolean hasMore;
public List<Map<String, String>> examples; public List<Map<String, Object>> examples;
public final List<CodegenProperty> headers = new ArrayList<CodegenProperty>(); public final List<CodegenProperty> headers = new ArrayList<CodegenProperty>();
public String dataType, baseType, containerType; public String dataType, baseType, containerType;
public Boolean isDefault;
public Boolean simpleType; public Boolean simpleType;
public Boolean primitiveType; public Boolean primitiveType;
public Boolean isMapContainer; public Boolean isMapContainer;
public Boolean isListContainer; public Boolean isListContainer;
public Object schema; public Object schema;
public String jsonSchema; public String jsonSchema;
} public boolean isWildcard() { return "0".equals(code) || "default".equals(code); }
}

View File

@ -1,11 +1,10 @@
package com.wordnik.swagger.codegen; package com.wordnik.swagger.codegen;
public class CodegenSecurity { public class CodegenSecurity {
String name; public String name;
String type; public String type;
Boolean hasMore, isBasic, isOAuth, isApiKey; public Boolean hasMore, isBasic, isOAuth, isApiKey;
// ApiKey specific // ApiKey specific
String keyParamName; public String keyParamName;
Boolean isKeyInQuery, isKeyInHeader; public Boolean isKeyInQuery, isKeyInHeader;
} }

View File

@ -1,26 +1,58 @@
package com.wordnik.swagger.codegen; package com.wordnik.swagger.codegen;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.wordnik.swagger.codegen.examples.ExampleGenerator; import com.wordnik.swagger.codegen.examples.ExampleGenerator;
import com.wordnik.swagger.models.*; import com.wordnik.swagger.models.ArrayModel;
import com.wordnik.swagger.models.Model;
import com.wordnik.swagger.models.ModelImpl;
import com.wordnik.swagger.models.Operation;
import com.wordnik.swagger.models.RefModel;
import com.wordnik.swagger.models.Response;
import com.wordnik.swagger.models.Swagger;
import com.wordnik.swagger.models.auth.ApiKeyAuthDefinition; import com.wordnik.swagger.models.auth.ApiKeyAuthDefinition;
import com.wordnik.swagger.models.auth.BasicAuthDefinition; import com.wordnik.swagger.models.auth.BasicAuthDefinition;
import com.wordnik.swagger.models.auth.In; import com.wordnik.swagger.models.auth.In;
import com.wordnik.swagger.models.auth.SecuritySchemeDefinition; import com.wordnik.swagger.models.auth.SecuritySchemeDefinition;
import com.wordnik.swagger.models.parameters.*; import com.wordnik.swagger.models.parameters.BodyParameter;
import com.wordnik.swagger.models.properties.*; import com.wordnik.swagger.models.parameters.CookieParameter;
import com.wordnik.swagger.models.parameters.FormParameter;
import com.wordnik.swagger.models.parameters.HeaderParameter;
import com.wordnik.swagger.models.parameters.Parameter;
import com.wordnik.swagger.models.parameters.PathParameter;
import com.wordnik.swagger.models.parameters.QueryParameter;
import com.wordnik.swagger.models.parameters.SerializableParameter;
import com.wordnik.swagger.models.properties.AbstractNumericProperty;
import com.wordnik.swagger.models.properties.ArrayProperty;
import com.wordnik.swagger.models.properties.BooleanProperty;
import com.wordnik.swagger.models.properties.DateProperty;
import com.wordnik.swagger.models.properties.DateTimeProperty;
import com.wordnik.swagger.models.properties.DecimalProperty;
import com.wordnik.swagger.models.properties.DoubleProperty;
import com.wordnik.swagger.models.properties.FloatProperty;
import com.wordnik.swagger.models.properties.IntegerProperty;
import com.wordnik.swagger.models.properties.LongProperty;
import com.wordnik.swagger.models.properties.MapProperty;
import com.wordnik.swagger.models.properties.Property;
import com.wordnik.swagger.models.properties.PropertyBuilder;
import com.wordnik.swagger.models.properties.RefProperty;
import com.wordnik.swagger.models.properties.StringProperty;
import com.wordnik.swagger.util.Json; import com.wordnik.swagger.util.Json;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DefaultCodegen { public class DefaultCodegen {
Logger LOGGER = LoggerFactory.getLogger(DefaultCodegen.class); Logger LOGGER = LoggerFactory.getLogger(DefaultCodegen.class);
@ -166,6 +198,9 @@ public class DefaultCodegen {
return name; return name;
} }
public String toEnumName(CodegenProperty property) {
return StringUtils.capitalize(property.name) + "Enum";
}
public String escapeReservedWord(String name) { public String escapeReservedWord(String name) {
throw new RuntimeException("reserved word " + name + " not allowed"); throw new RuntimeException("reserved word " + name + " not allowed");
@ -470,6 +505,7 @@ public class DefaultCodegen {
} }
if(impl.getProperties() != null && impl.getProperties().size() > 0) { if(impl.getProperties() != null && impl.getProperties().size() > 0) {
m.hasVars = true; m.hasVars = true;
m.hasEnums = false;
for(String key: impl.getProperties().keySet()) { for(String key: impl.getProperties().keySet()) {
Property prop = impl.getProperties().get(key); Property prop = impl.getProperties().get(key);
@ -497,6 +533,8 @@ public class DefaultCodegen {
} }
m.vars.add(cp); m.vars.add(cp);
count += 1; count += 1;
if (cp.isEnum)
m.hasEnums = true;
if(count != impl.getProperties().keySet().size()) if(count != impl.getProperties().keySet().size())
cp.hasMore = new Boolean(true); cp.hasMore = new Boolean(true);
if(cp.isContainer != null) { if(cp.isContainer != null) {
@ -589,7 +627,7 @@ public class DefaultCodegen {
// this can cause issues for clients which don't support enums // this can cause issues for clients which don't support enums
if(property.isEnum) if(property.isEnum)
property.datatypeWithEnum = StringUtils.capitalize(property.name) + "Enum"; property.datatypeWithEnum = toEnumName(property);
else else
property.datatypeWithEnum = property.datatype; property.datatypeWithEnum = property.datatype;
@ -692,11 +730,11 @@ public class DefaultCodegen {
for(String key: operation.getConsumes()) { for(String key: operation.getConsumes()) {
Map<String, String> mediaType = new HashMap<String, String>(); Map<String, String> mediaType = new HashMap<String, String>();
mediaType.put("mediaType", key); mediaType.put("mediaType", key);
count += 1;
if (count < operation.getConsumes().size()) if (count < operation.getConsumes().size())
mediaType.put("hasMore", "true"); mediaType.put("hasMore", "true");
else else
mediaType.put("hasMore", null); mediaType.put("hasMore", null);
count += 1;
c.add(mediaType); c.add(mediaType);
} }
op.consumes = c; op.consumes = c;
@ -722,7 +760,6 @@ public class DefaultCodegen {
if (operation.getResponses() != null && !operation.getResponses().isEmpty()) { if (operation.getResponses() != null && !operation.getResponses().isEmpty()) {
Response methodResponse = findMethodResponse(operation.getResponses()); Response methodResponse = findMethodResponse(operation.getResponses());
CodegenResponse methodCodegenResponse = null;
for (Map.Entry<String, Response> entry : operation.getResponses().entrySet()) { for (Map.Entry<String, Response> entry : operation.getResponses().entrySet()) {
Response response = entry.getValue(); Response response = entry.getValue();
@ -732,9 +769,7 @@ public class DefaultCodegen {
!defaultIncludes.contains(r.baseType) && !defaultIncludes.contains(r.baseType) &&
!languageSpecificPrimitives.contains(r.baseType)) !languageSpecificPrimitives.contains(r.baseType))
imports.add(r.baseType); imports.add(r.baseType);
r.isDefault = response == methodResponse;
if (response == methodResponse)
methodCodegenResponse = r;
op.responses.add(r); op.responses.add(r);
} }
op.responses.get(op.responses.size() - 1).hasMore = false; op.responses.get(op.responses.size() - 1).hasMore = false;
@ -904,6 +939,15 @@ public class DefaultCodegen {
p.required = param.getRequired(); p.required = param.getRequired();
p.jsonSchema = Json.pretty(param); p.jsonSchema = Json.pretty(param);
// move the defaultValue for headers, forms and params
if(param instanceof QueryParameter) {
p.defaultValue = ((QueryParameter)param).getDefaultValue();
} else if(param instanceof HeaderParameter) {
p.defaultValue = ((HeaderParameter)param).getDefaultValue();
} else if(param instanceof FormParameter) {
p.defaultValue = ((FormParameter)param).getDefaultValue();
}
if(param instanceof SerializableParameter) { if(param instanceof SerializableParameter) {
SerializableParameter qp = (SerializableParameter) param; SerializableParameter qp = (SerializableParameter) param;
Property property = null; Property property = null;
@ -918,6 +962,19 @@ public class DefaultCodegen {
collectionFormat = qp.getCollectionFormat(); collectionFormat = qp.getCollectionFormat();
CodegenProperty pr = fromProperty("inner", inner); CodegenProperty pr = fromProperty("inner", inner);
p.baseType = pr.datatype; p.baseType = pr.datatype;
p.isContainer = true;
imports.add(pr.baseType);
}
else if("object".equals(qp.getType())) {
Property inner = qp.getItems();
if(inner == null) {
LOGGER.warn("warning! No inner type supplied for map parameter \"" + qp.getName() + "\", using String");
inner = new StringProperty().description("//TODO automatically added by swagger-codegen");
}
property = new MapProperty(inner);
collectionFormat = qp.getCollectionFormat();
CodegenProperty pr = fromProperty("inner", inner);
p.baseType = pr.datatype;
imports.add(pr.baseType); imports.add(pr.baseType);
} }
else else
@ -926,6 +983,7 @@ public class DefaultCodegen {
LOGGER.warn("warning! Property type \"" + qp.getType() + "\" not found for parameter \"" + param.getName() + "\", using String"); LOGGER.warn("warning! Property type \"" + qp.getType() + "\" not found for parameter \"" + param.getName() + "\", using String");
property = new StringProperty().description("//TODO automatically added by swagger-codegen. Type was " + qp.getType() + " but not supported"); property = new StringProperty().description("//TODO automatically added by swagger-codegen. Type was " + qp.getType() + " but not supported");
} }
property.setRequired(param.getRequired());
CodegenProperty model = fromProperty(qp.getName(), property); CodegenProperty model = fromProperty(qp.getName(), property);
p.collectionFormat = collectionFormat; p.collectionFormat = collectionFormat;
p.dataType = model.datatype; p.dataType = model.datatype;
@ -949,6 +1007,7 @@ public class DefaultCodegen {
else { else {
// TODO: missing format, so this will not always work // TODO: missing format, so this will not always work
Property prop = PropertyBuilder.build(impl.getType(), null, null); Property prop = PropertyBuilder.build(impl.getType(), null, null);
prop.setRequired(bp.getRequired());
CodegenProperty cp = fromProperty("property", prop); CodegenProperty cp = fromProperty("property", prop);
if(cp != null) { if(cp != null) {
p.dataType = cp.datatype; p.dataType = cp.datatype;
@ -962,6 +1021,7 @@ public class DefaultCodegen {
CodegenModel cm = fromModel(bp.getName(), impl); CodegenModel cm = fromModel(bp.getName(), impl);
// get the single property // get the single property
ArrayProperty ap = new ArrayProperty().items(impl.getItems()); ArrayProperty ap = new ArrayProperty().items(impl.getItems());
ap.setRequired(param.getRequired());
CodegenProperty cp = fromProperty("inner", ap); CodegenProperty cp = fromProperty("inner", ap);
if(cp.complexType != null) { if(cp.complexType != null) {
imports.add(cp.complexType); imports.add(cp.complexType);
@ -996,9 +1056,9 @@ public class DefaultCodegen {
if(schemes == null) if(schemes == null)
return null; return null;
List<CodegenSecurity> secs = new ArrayList<CodegenSecurity>(); List<CodegenSecurity> secs = new ArrayList<CodegenSecurity>(schemes.size());
for(Iterator entries = schemes.entrySet().iterator(); entries.hasNext(); ) { for (Iterator<Map.Entry<String, SecuritySchemeDefinition>> it = schemes.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, SecuritySchemeDefinition> entry = (Map.Entry<String, SecuritySchemeDefinition>) entries.next(); final Map.Entry<String, SecuritySchemeDefinition> entry = it.next();
final SecuritySchemeDefinition schemeDefinition = entry.getValue(); final SecuritySchemeDefinition schemeDefinition = entry.getValue();
CodegenSecurity sec = CodegenModelFactory.newInstance(CodegenModelType.SECURITY); CodegenSecurity sec = CodegenModelFactory.newInstance(CodegenModelType.SECURITY);
@ -1018,23 +1078,21 @@ public class DefaultCodegen {
sec.isOAuth = !sec.isBasic; sec.isOAuth = !sec.isBasic;
} }
sec.hasMore = entries.hasNext(); sec.hasMore = it.hasNext();
secs.add(sec); secs.add(sec);
} }
return secs; return secs;
} }
protected List<Map<String, String>> toExamples(Map<String, String> examples) { protected List<Map<String, Object>> toExamples(Map<String, Object> examples) {
if(examples == null) if(examples == null)
return null; return null;
List<Map<String, String>> output = new ArrayList<Map<String, String>>(); final List<Map<String, Object>> output = new ArrayList<Map<String, Object>>(examples.size());
for(String key: examples.keySet()) { for(Map.Entry<String, Object> entry : examples.entrySet()) {
String value = examples.get(key); final Map<String, Object> kv = new HashMap<String, Object>();
kv.put("contentType", entry.getKey());
Map<String, String> kv = new HashMap<String, String>(); kv.put("example", entry.getValue());
kv.put("contentType", key);
kv.put("example", value);
output.add(kv); output.add(kv);
} }
return output; return output;
@ -1169,4 +1227,13 @@ public class DefaultCodegen {
} }
public String apiFilename(String templateName, String tag)
{
String suffix = apiTemplateFiles().get(templateName);
return apiFileFolder() + File.separator + toApiFilename(tag) + suffix;
}
public boolean shouldOverwrite( String filename ){
return true;
}
} }

View File

@ -165,6 +165,10 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
operation.putAll(config.additionalProperties()); operation.putAll(config.additionalProperties());
operation.put("classname", config.toApiName(tag)); operation.put("classname", config.toApiName(tag));
operation.put("classVarName", config.toApiVarName(tag)); operation.put("classVarName", config.toApiVarName(tag));
operation.put("importPath", config.toApiImport(tag));
processMimeTypes(swagger.getConsumes(), operation, "consumes");
processMimeTypes(swagger.getProduces(), operation, "produces");
allOperations.add(new HashMap<String, Object>(operation)); allOperations.add(new HashMap<String, Object>(operation));
for (int i = 0; i < allOperations.size(); i++) { for (int i = 0; i < allOperations.size(); i++) {
@ -175,11 +179,11 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
} }
for (String templateName : config.apiTemplateFiles().keySet()) { for (String templateName : config.apiTemplateFiles().keySet()) {
String suffix = config.apiTemplateFiles().get(templateName);
String filename = config.apiFileFolder() + String filename = config.apiFilename( templateName, tag );
File.separator + if( new File( filename ).exists() && !config.shouldOverwrite( filename )){
config.toApiFilename(tag) + continue;
suffix; }
String template = readTemplate(config.templateDir() + File.separator + templateName); String template = readTemplate(config.templateDir() + File.separator + templateName);
Template tmpl = Mustache.compiler() Template tmpl = Mustache.compiler()
@ -217,6 +221,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
bundle.put("models", allModels); bundle.put("models", allModels);
bundle.put("apiFolder", config.apiPackage().replace('.', File.separatorChar)); bundle.put("apiFolder", config.apiPackage().replace('.', File.separatorChar));
bundle.put("modelPackage", config.modelPackage()); bundle.put("modelPackage", config.modelPackage());
bundle.put("authMethods", config.fromSecurity(swagger.getSecurityDefinitions()));
if (swagger.getExternalDocs() != null) { if (swagger.getExternalDocs() != null) {
bundle.put("externalDocs", swagger.getExternalDocs()); bundle.put("externalDocs", swagger.getExternalDocs());
} }
@ -291,6 +296,28 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
return files; return files;
} }
private void processMimeTypes(List<String> mimeTypeList, Map<String, Object> operation, String source) {
if(mimeTypeList != null && mimeTypeList.size() > 0) {
List<Map<String, String>> c = new ArrayList<Map<String, String>>();
int count = 0;
for(String key: mimeTypeList) {
Map<String, String> mediaType = new HashMap<String, String>();
mediaType.put("mediaType", key);
count += 1;
if (count < mimeTypeList.size()) {
mediaType.put("hasMore", "true");
}
else {
mediaType.put("hasMore", null);
}
c.add(mediaType);
}
operation.put(source, c);
String flagFieldName = "has" + source.substring(0, 1).toUpperCase() + source.substring(1);
operation.put(flagFieldName, true);
}
}
public Map<String, List<CodegenOperation>> processPaths(Map<String, Path> paths) { public Map<String, List<CodegenOperation>> processPaths(Map<String, Path> paths) {
Map<String, List<CodegenOperation>> ops = new HashMap<String, List<CodegenOperation>>(); Map<String, List<CodegenOperation>> ops = new HashMap<String, List<CodegenOperation>>();
@ -427,6 +454,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
CodegenModel cm = config.fromModel(key, mm); CodegenModel cm = config.fromModel(key, mm);
Map<String, Object> mo = new HashMap<String, Object>(); Map<String, Object> mo = new HashMap<String, Object>();
mo.put("model", cm); mo.put("model", cm);
mo.put("importPath", config.toModelImport(key));
models.add(mo); models.add(mo);
allImports.addAll(cm.imports); allImports.addAll(cm.imports);
} }

View File

@ -1,19 +1,33 @@
package com.wordnik.swagger.codegen.examples; package com.wordnik.swagger.codegen.examples;
import com.wordnik.swagger.models.*;
import com.wordnik.swagger.models.properties.*;
import com.wordnik.swagger.util.Json;
import java.text.SimpleDateFormat;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.wordnik.swagger.models.Model;
import com.wordnik.swagger.models.ModelImpl;
import com.wordnik.swagger.models.properties.ArrayProperty;
import com.wordnik.swagger.models.properties.BooleanProperty;
import com.wordnik.swagger.models.properties.DateProperty;
import com.wordnik.swagger.models.properties.DateTimeProperty;
import com.wordnik.swagger.models.properties.DecimalProperty;
import com.wordnik.swagger.models.properties.DoubleProperty;
import com.wordnik.swagger.models.properties.FileProperty;
import com.wordnik.swagger.models.properties.FloatProperty;
import com.wordnik.swagger.models.properties.IntegerProperty;
import com.wordnik.swagger.models.properties.LongProperty;
import com.wordnik.swagger.models.properties.MapProperty;
import com.wordnik.swagger.models.properties.ObjectProperty;
import com.wordnik.swagger.models.properties.Property;
import com.wordnik.swagger.models.properties.RefProperty;
import com.wordnik.swagger.models.properties.StringProperty;
import com.wordnik.swagger.models.properties.UUIDProperty;
import com.wordnik.swagger.util.Json;
public class ExampleGenerator { public class ExampleGenerator {
protected Map<String, Model> examples; protected Map<String, Model> examples;
@ -22,7 +36,7 @@ public class ExampleGenerator {
this.examples = examples; this.examples = examples;
} }
public List<Map<String, String>> generate(Map<String, String> examples, List<String> mediaTypes, Property property) { public List<Map<String, String>> generate(Map<String, Object> examples, List<String> mediaTypes, Property property) {
List<Map<String, String>> output = new ArrayList<Map<String, String>>(); List<Map<String, String>> output = new ArrayList<Map<String, String>>();
Set<String> processedModels = new HashSet<String>(); Set<String> processedModels = new HashSet<String>();
if(examples == null ) { if(examples == null ) {
@ -37,7 +51,6 @@ public class ExampleGenerator {
String example = Json.pretty(resolvePropertyToExample(mediaType, property, processedModels)); String example = Json.pretty(resolvePropertyToExample(mediaType, property, processedModels));
if(example != null) { if(example != null) {
example = example.replaceAll("\n", "\\\\n");
kv.put("example", example); kv.put("example", example);
output.add(kv); output.add(kv);
} }
@ -45,7 +58,6 @@ public class ExampleGenerator {
else if(property != null && mediaType.startsWith("application/xml")) { else if(property != null && mediaType.startsWith("application/xml")) {
String example = new XmlExampleGenerator(this.examples).toXml(property); String example = new XmlExampleGenerator(this.examples).toXml(property);
if(example != null) { if(example != null) {
example = example.replaceAll("\n", "\\\\n");
kv.put("example", example); kv.put("example", example);
output.add(kv); output.add(kv);
} }
@ -53,12 +65,10 @@ public class ExampleGenerator {
} }
} }
else { else {
for(String key: examples.keySet()) { for(Map.Entry<String, Object> entry: examples.entrySet()) {
String value = examples.get(key); final Map<String, String> kv = new HashMap<String, String>();
kv.put("contentType", entry.getKey());
Map<String, String> kv = new HashMap<String, String>(); kv.put("example", Json.pretty(entry.getValue()));
kv.put("contentType", key);
kv.put("example", value);
output.add(kv); output.add(kv);
} }
} }

View File

@ -8,6 +8,8 @@ import com.wordnik.swagger.models.properties.*;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import org.codehaus.plexus.util.StringUtils;
public class XmlExampleGenerator { public class XmlExampleGenerator {
public static String NEWLINE = "\n"; public static String NEWLINE = "\n";
public static String TAG_START = "<"; public static String TAG_START = "<";
@ -16,6 +18,7 @@ public class XmlExampleGenerator {
protected Map<String, Model> examples; protected Map<String, Model> examples;
protected SimpleDateFormat dtFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); protected SimpleDateFormat dtFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
protected SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); protected SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
private static String EMPTY = "";
public XmlExampleGenerator(Map<String, Model> examples) { public XmlExampleGenerator(Map<String, Model> examples) {
this.examples = examples; this.examples = examples;
@ -24,29 +27,36 @@ public class XmlExampleGenerator {
} }
public String toXml(Property property) { public String toXml(Property property) {
return toXml(null, property, 0); return toXml(null, property, 0, Collections.<String>emptySet());
} }
protected String toXml(Model model, int indent) { protected String toXml(Model model, int indent, Collection<String> path) {
if(model instanceof RefModel) { if(model instanceof RefModel) {
RefModel ref = (RefModel) model; RefModel ref = (RefModel) model;
Model actualModel = examples.get(ref.getSimpleRef()); Model actualModel = examples.get(ref.getSimpleRef());
if(actualModel instanceof ModelImpl) if(actualModel instanceof ModelImpl)
return modelImplToXml((ModelImpl)actualModel, indent); return modelImplToXml((ModelImpl) actualModel, indent, path);
} }
else if(model instanceof ModelImpl) { else if(model instanceof ModelImpl) {
return modelImplToXml((ModelImpl)model, indent); return modelImplToXml((ModelImpl) model, indent, path);
} }
return null; return null;
} }
protected String modelImplToXml(ModelImpl model, int indent) { protected String modelImplToXml(ModelImpl model, int indent, Collection<String> path) {
final String modelName = model.getName();
if (path.contains(modelName)) {
return EMPTY;
}
final Set<String> selfPath = new HashSet<String>(path);
selfPath.add(modelName);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
// attributes // attributes
Map<String, Property> attributes = new LinkedHashMap<String, Property>(); Map<String, Property> attributes = new LinkedHashMap<String, Property>();
Map<String, Property> elements = new LinkedHashMap<String, Property>(); Map<String, Property> elements = new LinkedHashMap<String, Property>();
String name = model.getName(); String name = modelName;
String namespace; String namespace;
String prefix; String prefix;
Boolean wrapped; Boolean wrapped;
@ -67,13 +77,17 @@ public class XmlExampleGenerator {
sb.append(name); sb.append(name);
for(String pName : attributes.keySet()) { for(String pName : attributes.keySet()) {
Property p = attributes.get(pName); Property p = attributes.get(pName);
sb.append(" ").append(pName).append("=").append(quote(toXml(null, p, 0))); sb.append(" ").append(pName).append("=").append(quote(toXml(null, p, 0, selfPath)));
} }
sb.append(CLOSE_TAG); sb.append(CLOSE_TAG);
sb.append(NEWLINE); sb.append(NEWLINE);
for(String pName : elements.keySet()) { for(String pName : elements.keySet()) {
Property p = elements.get(pName); Property p = elements.get(pName);
sb.append(toXml(pName, p, indent + 1)); final String asXml = toXml(pName, p, indent + 1, selfPath);
if (StringUtils.isEmpty(asXml)) {
continue;
}
sb.append(asXml);
sb.append(NEWLINE); sb.append(NEWLINE);
} }
sb.append(indent(indent)).append(TAG_END).append(name).append(CLOSE_TAG); sb.append(indent(indent)).append(TAG_END).append(name).append(CLOSE_TAG);
@ -85,7 +99,7 @@ public class XmlExampleGenerator {
return "\"" + string + "\""; return "\"" + string + "\"";
} }
protected String toXml(String name, Property property, int indent) { protected String toXml(String name, Property property, int indent, Collection<String> path) {
if(property == null) { if(property == null) {
return ""; return "";
} }
@ -98,12 +112,16 @@ public class XmlExampleGenerator {
if(property.getXml() != null && property.getXml().getWrapped()) if(property.getXml() != null && property.getXml().getWrapped())
wrapped = true; wrapped = true;
if(wrapped) { if(wrapped) {
String prefix = EMPTY;
if(name != null) { if(name != null) {
sb.append(indent(indent)); sb.append(indent(indent));
sb.append(openTag(name)); sb.append(openTag(name));
sb.append(NEWLINE); prefix = NEWLINE;
}
final String asXml = toXml(name, inner, indent + 1, path);
if (StringUtils.isNotEmpty(asXml)) {
sb.append(prefix).append(asXml);
} }
sb.append(toXml(name, inner, indent + 1));
if(name != null) { if(name != null) {
sb.append(NEWLINE); sb.append(NEWLINE);
sb.append(indent(indent)); sb.append(indent(indent));
@ -111,12 +129,12 @@ public class XmlExampleGenerator {
} }
} }
else else
sb.append(toXml(name, inner, indent)); sb.append(toXml(name, inner, indent, path));
} }
else if(property instanceof RefProperty) { else if(property instanceof RefProperty) {
RefProperty ref = (RefProperty) property; RefProperty ref = (RefProperty) property;
Model actualModel = examples.get(ref.getSimpleRef()); Model actualModel = examples.get(ref.getSimpleRef());
sb.append(toXml(actualModel, indent)); sb.append(toXml(actualModel, indent, path));
} }
else { else {
if(name != null) { if(name != null) {

View File

@ -0,0 +1,365 @@
package com.wordnik.swagger.codegen.languages;
import com.google.common.base.CaseFormat;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import com.wordnik.swagger.codegen.*;
import com.wordnik.swagger.models.auth.SecuritySchemeDefinition;
import com.wordnik.swagger.models.properties.*;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.*;
public class AkkaScalaClientCodegen extends DefaultCodegen implements CodegenConfig {
Logger LOGGER = LoggerFactory.getLogger(AkkaScalaClientCodegen.class);
protected String mainPackage = "io.swagger.client";
protected String invokerPackage = mainPackage + ".core";
protected String groupId = "com.wordnik";
protected String artifactId = "swagger-client";
protected String artifactVersion = "1.0.0";
protected String sourceFolder = "src/main/scala";
protected String resourcesFolder = "src/main/resources";
protected String configKey = "apiRequest";
protected int defaultTimeoutInMs = 5000;
protected String configKeyPath = mainPackage;
protected boolean registerNonStandardStatusCodes = true;
protected boolean renderJavadoc = true;
protected boolean removeOAuthSecurities = true;
/**
* If set to true, only the default response (the one with le lowest 2XX code) will be considered as a success, and all
* others as ApiErrors.
* If set to false, all responses defined in the model will be considered as a success upon reception. Only http errors,
* unmarshalling problems and any other RuntimeException will be considered as ApiErrors.
*/
protected boolean onlyOneSuccess = true;
public CodegenType getTag() {
return CodegenType.CLIENT;
}
public String getName() {
return "akka-scala";
}
public String getHelp() {
return "Generates a Scala client library base on Akka/Spray.";
}
public AkkaScalaClientCodegen() {
super();
outputFolder = "generated-code/scala";
modelTemplateFiles.put("model.mustache", ".scala");
apiTemplateFiles.put("api.mustache", ".scala");
templateDir = "akka-scala";
apiPackage = mainPackage + ".api";
modelPackage = mainPackage + ".model";
reservedWords = new HashSet<String>(
Arrays.asList(
"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")
);
additionalProperties.put("invokerPackage", invokerPackage);
additionalProperties.put("groupId", groupId);
additionalProperties.put("artifactId", artifactId);
additionalProperties.put("artifactVersion", artifactVersion);
additionalProperties.put("configKey", configKey);
additionalProperties.put("configKeyPath", configKeyPath);
additionalProperties.put("defaultTimeout", defaultTimeoutInMs);
if (renderJavadoc)
additionalProperties.put("javadocRenderer", new JavadocLambda());
additionalProperties.put("fnCapitalize", new CapitalizeLambda());
additionalProperties.put("fnCamelize", new CamelizeLambda(false));
additionalProperties.put("fnEnumEntry", new EnumEntryLambda());
additionalProperties.put("onlyOneSuccess", onlyOneSuccess);
supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml"));
supportingFiles.add(new SupportingFile("reference.mustache", resourcesFolder, "reference.conf"));
final String invokerFolder = (sourceFolder + File.separator + invokerPackage).replace(".", File.separator);
supportingFiles.add(new SupportingFile("apiRequest.mustache", invokerFolder, "ApiRequest.scala"));
supportingFiles.add(new SupportingFile("apiInvoker.mustache", invokerFolder, "ApiInvoker.scala"));
supportingFiles.add(new SupportingFile("requests.mustache", invokerFolder, "requests.scala"));
supportingFiles.add(new SupportingFile("apiSettings.mustache", invokerFolder, "ApiSettings.scala"));
final String apiFolder = (sourceFolder + File.separator + apiPackage).replace(".", File.separator);
supportingFiles.add(new SupportingFile("enumsSerializers.mustache", apiFolder, "EnumsSerializers.scala"));
importMapping.remove("Seq");
importMapping.remove("List");
importMapping.remove("Set");
importMapping.remove("Map");
importMapping.put("DateTime", "org.joda.time.DateTime");
typeMapping = new HashMap<String, String>();
typeMapping.put("array", "Seq");
typeMapping.put("set", "Set");
typeMapping.put("boolean", "Boolean");
typeMapping.put("string", "String");
typeMapping.put("int", "Int");
typeMapping.put("integer", "Int");
typeMapping.put("long", "Long");
typeMapping.put("float", "Float");
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");
typeMapping.put("number", "Double");
languageSpecificPrimitives = new HashSet<String>(
Arrays.asList(
"String",
"boolean",
"Boolean",
"Double",
"Int",
"Long",
"Float",
"Object",
"List",
"Seq",
"Map")
);
instantiationTypes.put("array", "ListBuffer");
instantiationTypes.put("map", "Map");
}
@Override
public String escapeReservedWord(String name) {
return "`" + name + "`";
}
@Override
public String apiFileFolder() {
return outputFolder + "/" + sourceFolder + "/" + apiPackage().replace('.', File.separatorChar);
}
public String modelFileFolder() {
return outputFolder + "/" + sourceFolder + "/" + modelPackage().replace('.', File.separatorChar);
}
@Override
public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
if (registerNonStandardStatusCodes) {
try {
@SuppressWarnings("unchecked")
Map<String, ArrayList<CodegenOperation>> opsMap = (Map<String, ArrayList<CodegenOperation>>) objs.get("operations");
HashSet<Integer> unknownCodes = new HashSet<Integer>();
for (CodegenOperation operation : opsMap.get("operation")) {
for (CodegenResponse response : operation.responses) {
if ("default".equals(response.code))
continue;
try {
int code = Integer.parseInt(response.code);
if (code >= 600) {
unknownCodes.add(code);
}
} catch (NumberFormatException e) {
LOGGER.error("Status code is not an integer : response.code", e);
}
}
}
if (!unknownCodes.isEmpty()) {
additionalProperties.put("unknownStatusCodes", unknownCodes);
}
} catch (Exception e) {
LOGGER.error("Unable to find operations List", e);
}
}
return super.postProcessOperations(objs);
}
@Override
public String getTypeDeclaration(Property p) {
if (p instanceof ArrayProperty) {
ArrayProperty ap = (ArrayProperty) p;
Property inner = ap.getItems();
return getSwaggerType(p) + "[" + getTypeDeclaration(inner) + "]";
} else if (p instanceof MapProperty) {
MapProperty mp = (MapProperty) p;
Property inner = mp.getAdditionalProperties();
return getSwaggerType(p) + "[String, " + getTypeDeclaration(inner) + "]";
}
return super.getTypeDeclaration(p);
}
@Override
public List<CodegenSecurity> fromSecurity(Map<String, SecuritySchemeDefinition> schemes) {
final List<CodegenSecurity> codegenSecurities = super.fromSecurity(schemes);
if (!removeOAuthSecurities)
return codegenSecurities;
// Remove OAuth securities
Iterator<CodegenSecurity> it = codegenSecurities.iterator();
while (it.hasNext()) {
final CodegenSecurity security = it.next();
if (security.isOAuth)
it.remove();
}
// Adapt 'hasMore'
it = codegenSecurities.iterator();
while (it.hasNext()) {
final CodegenSecurity security = it.next();
security.hasMore = it.hasNext();
}
if (codegenSecurities.isEmpty())
return null;
return codegenSecurities;
}
@Override
public String toOperationId(String operationId) {
return super.toOperationId(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, operationId));
}
private String formatIdentifier(String name, boolean capitalized) {
String identifier = camelize(name, true);
if (capitalized)
identifier = StringUtils.capitalize(identifier);
if (identifier.matches("[a-zA-Z_$][\\w_$]+") && !reservedWords.contains(identifier))
return identifier;
return escapeReservedWord(identifier);
}
@Override
public String toParamName(String name) { return formatIdentifier(name, false); }
@Override
public String toVarName(String name) {
return formatIdentifier(name, false);
}
@Override
public String toEnumName(CodegenProperty property)
{
return formatIdentifier(property.baseName, true);
}
@Override
public String getSwaggerType(Property p) {
String swaggerType = super.getSwaggerType(p);
String type;
if (typeMapping.containsKey(swaggerType)) {
type = typeMapping.get(swaggerType);
if (languageSpecificPrimitives.contains(type))
return toModelName(type);
} else
type = swaggerType;
return toModelName(type);
}
@Override
public String toInstantiationType(Property p) {
if (p instanceof MapProperty) {
MapProperty ap = (MapProperty) p;
String inner = getSwaggerType(ap.getAdditionalProperties());
return instantiationTypes.get("map") + "[String, " + inner + "]";
} else if (p instanceof ArrayProperty) {
ArrayProperty ap = (ArrayProperty) p;
String inner = getSwaggerType(ap.getItems());
return instantiationTypes.get("array") + "[" + inner + "]";
} else
return null;
}
public String toDefaultValue(Property p) {
if (!p.getRequired())
return "None";
if (p instanceof StringProperty)
return "null";
else if (p instanceof BooleanProperty)
return "null";
else if (p instanceof DateProperty)
return "null";
else if (p instanceof DateTimeProperty)
return "null";
else if (p instanceof DoubleProperty)
return "null";
else if (p instanceof FloatProperty)
return "null";
else if (p instanceof IntegerProperty)
return "null";
else if (p instanceof LongProperty)
return "null";
else if (p instanceof MapProperty) {
MapProperty ap = (MapProperty) p;
String inner = getSwaggerType(ap.getAdditionalProperties());
return "Map[String, " + inner + "].empty ";
} else if (p instanceof ArrayProperty) {
ArrayProperty ap = (ArrayProperty) p;
String inner = getSwaggerType(ap.getItems());
return "Seq[" + inner + "].empty ";
} else
return "null";
}
private static abstract class CustomLambda implements Mustache.Lambda {
@Override
public void execute(Template.Fragment frag, Writer out) throws IOException {
final StringWriter tempWriter = new StringWriter();
frag.execute(tempWriter);
out.write(formatFragment(tempWriter.toString()));
}
public abstract String formatFragment(String fragment);
}
private static class JavadocLambda extends CustomLambda {
@Override
public String formatFragment(String fragment) {
final String[] lines = fragment.split("\\r?\\n");
final StringBuilder sb = new StringBuilder();
sb.append(" /**\n");
for (String line : lines) {
sb.append(" * ").append(line).append("\n");
}
sb.append(" */\n");
return sb.toString();
}
}
private static class CapitalizeLambda extends CustomLambda {
@Override
public String formatFragment(String fragment) {
return StringUtils.capitalize(fragment);
}
}
private static class CamelizeLambda extends CustomLambda {
private final boolean capitalizeFirst;
public CamelizeLambda(boolean capitalizeFirst) {
this.capitalizeFirst = capitalizeFirst;
}
@Override
public String formatFragment(String fragment) {
return camelize(fragment, !capitalizeFirst);
}
}
private class EnumEntryLambda extends CustomLambda {
@Override
public String formatFragment(String fragment) {
return formatIdentifier(fragment, true);
}
}
}

View File

@ -13,6 +13,7 @@ public class AndroidClientCodegen extends DefaultCodegen implements CodegenConfi
protected String artifactVersion = "1.0.0"; protected String artifactVersion = "1.0.0";
protected String projectFolder = "src/main"; protected String projectFolder = "src/main";
protected String sourceFolder = projectFolder + "/java"; protected String sourceFolder = projectFolder + "/java";
protected Boolean useAndroidMavenGradlePlugin = true;
public CodegenType getTag() { public CodegenType getTag() {
return CodegenType.CLIENT; return CodegenType.CLIENT;
@ -52,6 +53,9 @@ public class AndroidClientCodegen extends DefaultCodegen implements CodegenConfi
additionalProperties.put("artifactVersion", artifactVersion); additionalProperties.put("artifactVersion", artifactVersion);
supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml")); supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml"));
additionalProperties.put("useAndroidMavenGradlePlugin", useAndroidMavenGradlePlugin);
supportingFiles.add(new SupportingFile("settings.gradle.mustache", "", "settings.gradle"));
supportingFiles.add(new SupportingFile("build.mustache", "", "build.gradle")); supportingFiles.add(new SupportingFile("build.mustache", "", "build.gradle"));
supportingFiles.add(new SupportingFile("manifest.mustache", projectFolder, "AndroidManifest.xml")); supportingFiles.add(new SupportingFile("manifest.mustache", projectFolder, "AndroidManifest.xml"));
supportingFiles.add(new SupportingFile("apiInvoker.mustache", supportingFiles.add(new SupportingFile("apiInvoker.mustache",

View File

@ -7,7 +7,7 @@ import java.util.*;
import java.io.File; import java.io.File;
public class CSharpClientCodegen extends DefaultCodegen implements CodegenConfig { public class CSharpClientCodegen extends DefaultCodegen implements CodegenConfig {
protected String invokerPackage = "io.swagger.client"; protected String invokerPackage = "IO.Swagger.Client";
protected String groupId = "io.swagger"; protected String groupId = "io.swagger";
protected String artifactId = "swagger-csharp-client"; protected String artifactId = "swagger-csharp-client";
protected String artifactVersion = "1.0.0"; protected String artifactVersion = "1.0.0";
@ -31,8 +31,8 @@ public class CSharpClientCodegen extends DefaultCodegen implements CodegenConfig
modelTemplateFiles.put("model.mustache", ".cs"); modelTemplateFiles.put("model.mustache", ".cs");
apiTemplateFiles.put("api.mustache", ".cs"); apiTemplateFiles.put("api.mustache", ".cs");
templateDir = "csharp"; templateDir = "csharp";
apiPackage = "io.swagger.Api"; apiPackage = "IO.Swagger.Api";
modelPackage = "io.swagger.Model"; modelPackage = "IO.Swagger.Model";
reservedWords = new HashSet<String> ( reservedWords = new HashSet<String> (
Arrays.asList( Arrays.asList(
@ -80,7 +80,7 @@ public class CSharpClientCodegen extends DefaultCodegen implements CodegenConfig
typeMapping.put("double", "double?"); typeMapping.put("double", "double?");
typeMapping.put("number", "double?"); typeMapping.put("number", "double?");
typeMapping.put("Date", "DateTime"); typeMapping.put("Date", "DateTime");
typeMapping.put("file", "byte[]"); typeMapping.put("file", "string"); // path to file
typeMapping.put("array", "List"); typeMapping.put("array", "List");
typeMapping.put("map", "Dictionary"); typeMapping.put("map", "Dictionary");

View File

@ -130,7 +130,6 @@ public class JavaClientCodegen extends DefaultCodegen implements CodegenConfig {
return toModelName(name); return toModelName(name);
} }
@Override @Override
public String getTypeDeclaration(Property p) { public String getTypeDeclaration(Property p) {
if(p instanceof ArrayProperty) { if(p instanceof ArrayProperty) {

View File

@ -1,8 +1,6 @@
package com.wordnik.swagger.codegen.languages; package com.wordnik.swagger.codegen.languages;
import com.wordnik.swagger.models.Operation; import com.wordnik.swagger.models.Operation;
import com.wordnik.swagger.models.Path;
import com.wordnik.swagger.util.Json;
import com.wordnik.swagger.codegen.*; import com.wordnik.swagger.codegen.*;
import com.wordnik.swagger.models.properties.*; import com.wordnik.swagger.models.properties.*;
@ -14,7 +12,6 @@ public class JaxRSServerCodegen extends JavaClientCodegen implements CodegenConf
protected String groupId = "io.swagger"; protected String groupId = "io.swagger";
protected String artifactId = "swagger-jaxrs-server"; protected String artifactId = "swagger-jaxrs-server";
protected String artifactVersion = "1.0.0"; protected String artifactVersion = "1.0.0";
protected String sourceFolder = "src/main/java";
protected String title = "Swagger Server"; protected String title = "Swagger Server";
public CodegenType getTag() { public CodegenType getTag() {
@ -31,12 +28,19 @@ public class JaxRSServerCodegen extends JavaClientCodegen implements CodegenConf
public JaxRSServerCodegen() { public JaxRSServerCodegen() {
super(); super();
outputFolder = "generated-code/javaJaxRS";
sourceFolder = "src/gen/java";
outputFolder = System.getProperty( "swagger.codegen.jaxrs.genfolder", "generated-code/javaJaxRS" );
modelTemplateFiles.put("model.mustache", ".java"); modelTemplateFiles.put("model.mustache", ".java");
apiTemplateFiles.put("api.mustache", ".java"); apiTemplateFiles.put("api.mustache", ".java");
apiTemplateFiles.put("apiService.mustache", ".java");
apiTemplateFiles.put("apiServiceImpl.mustache", ".java");
apiTemplateFiles.put("apiServiceFactory.mustache", ".java");
templateDir = "JavaJaxRS"; templateDir = "JavaJaxRS";
apiPackage = "io.swagger.api"; apiPackage = System.getProperty( "swagger.codegen.jaxrs.apipackage", "io.swagger.api") ;
modelPackage = "io.swagger.model"; modelPackage = System.getProperty( "swagger.codegen.jaxrs.modelpackage", "io.swagger.model" );
additionalProperties.put("invokerPackage", invokerPackage); additionalProperties.put("invokerPackage", invokerPackage);
additionalProperties.put("groupId", groupId); additionalProperties.put("groupId", groupId);
@ -146,4 +150,44 @@ public class JaxRSServerCodegen extends JavaClientCodegen implements CodegenConf
} }
return objs; return objs;
} }
@Override
public String apiFilename(String templateName, String tag) {
String result = super.apiFilename(templateName, tag);
if( templateName.endsWith( "Impl.mustache")){
int ix = result.lastIndexOf( '/' );
result = result.substring( 0, ix ) + "/impl" + result.substring( ix, result.length()-5 ) + "ServiceImpl.java";
String output = System.getProperty( "swagger.codegen.jaxrs.impl.source" );
if( output != null ){
result = result.replace( apiFileFolder(), implFileFolder(output));
}
}
else if( templateName.endsWith( "Factory.mustache")){
int ix = result.lastIndexOf( '/' );
result = result.substring( 0, ix ) + "/factories" + result.substring( ix, result.length()-5 ) + "ServiceFactory.java";
String output = System.getProperty( "swagger.codegen.jaxrs.impl.source" );
if( output != null ){
result = result.replace( apiFileFolder(), implFileFolder(output));
}
}
else if( templateName.endsWith( "Service.mustache")) {
int ix = result.lastIndexOf('.');
result = result.substring(0, ix) + "Service.java";
}
return result;
}
private String implFileFolder(String output) {
return outputFolder + "/" + output + "/" + apiPackage().replace('.', File.separatorChar);
}
public boolean shouldOverwrite( String filename ){
return !filename.endsWith( "ServiceImpl.java") && !filename.endsWith( "ServiceFactory.java");
}
} }

View File

@ -1,11 +1,19 @@
package com.wordnik.swagger.codegen.languages; package com.wordnik.swagger.codegen.languages;
import com.wordnik.swagger.codegen.*;
import com.wordnik.swagger.util.Json;
import com.wordnik.swagger.models.properties.*;
import java.util.*;
import java.io.File; import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.wordnik.swagger.codegen.CodegenConfig;
import com.wordnik.swagger.codegen.CodegenOperation;
import com.wordnik.swagger.codegen.CodegenParameter;
import com.wordnik.swagger.codegen.CodegenResponse;
import com.wordnik.swagger.codegen.CodegenType;
import com.wordnik.swagger.codegen.DefaultCodegen;
import com.wordnik.swagger.codegen.SupportingFile;
public class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig { public class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig {
protected String apiVersion = "1.0.0"; protected String apiVersion = "1.0.0";
@ -156,8 +164,10 @@ public class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig
@Override @Override
public Map<String, Object> postProcessOperations(Map<String, Object> objs) { public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
Map<String, Object> objectMap = (Map<String, Object>)objs.get("operations"); @SuppressWarnings("unchecked")
List<CodegenOperation> operations = (List<CodegenOperation>)objectMap.get("operation"); Map<String, Object> objectMap = (Map<String, Object>) objs.get("operations");
@SuppressWarnings("unchecked")
List<CodegenOperation> operations = (List<CodegenOperation>) objectMap.get("operation");
for(CodegenOperation operation : operations) { for(CodegenOperation operation : operations) {
operation.httpMethod = operation.httpMethod.toLowerCase(); operation.httpMethod = operation.httpMethod.toLowerCase();
List<CodegenParameter> params = operation.allParams; List<CodegenParameter> params = operation.allParams;
@ -170,20 +180,14 @@ public class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig
resp.code = "default"; resp.code = "default";
} }
} }
if(operation.examples != null && operation.examples.size() > 0) { if(operation.examples != null && !operation.examples.isEmpty()) {
List<Map<String, String>> examples = operation.examples; // Leave application/json* items only
for(int i = examples.size() - 1; i >= 0; i--) { for (Iterator<Map<String, String>> it = operation.examples.iterator(); it.hasNext();) {
Map<String, String> example = examples.get(i); final Map<String, String> example = it.next();
String contentType = example.get("contentType"); final String contentType = example.get("contentType");
if(contentType != null && contentType.indexOf("application/json") == 0) { if (contentType == null || !contentType.startsWith("application/json")) {
String jsonExample = example.get("example"); it.remove();
if(jsonExample != null) {
jsonExample = jsonExample.replaceAll("\\\\n", "\n");
example.put("example", jsonExample);
}
} }
else
examples.remove(i);
} }
} }
} }

View File

@ -120,8 +120,8 @@ public class ObjcClientCodegen extends DefaultCodegen implements CodegenConfig {
supportingFiles.add(new SupportingFile("SWGApiClient.m", sourceFolder, "SWGApiClient.m")); supportingFiles.add(new SupportingFile("SWGApiClient.m", sourceFolder, "SWGApiClient.m"));
supportingFiles.add(new SupportingFile("SWGFile.h", sourceFolder, "SWGFile.h")); supportingFiles.add(new SupportingFile("SWGFile.h", sourceFolder, "SWGFile.h"));
supportingFiles.add(new SupportingFile("SWGFile.m", sourceFolder, "SWGFile.m")); supportingFiles.add(new SupportingFile("SWGFile.m", sourceFolder, "SWGFile.m"));
supportingFiles.add(new SupportingFile("SWGDate.h", sourceFolder, "SWGDate.h")); supportingFiles.add(new SupportingFile("JSONValueTransformer+ISO8601.m", sourceFolder, "JSONValueTransformer+ISO8601.m"));
supportingFiles.add(new SupportingFile("SWGDate.m", sourceFolder, "SWGDate.m")); supportingFiles.add(new SupportingFile("JSONValueTransformer+ISO8601.h", sourceFolder, "JSONValueTransformer+ISO8601.h"));
supportingFiles.add(new SupportingFile("Podfile.mustache", "", "Podfile")); supportingFiles.add(new SupportingFile("Podfile.mustache", "", "Podfile"));
} }

View File

@ -84,6 +84,7 @@ public class PhpClientCodegen extends DefaultCodegen implements CodegenConfig {
typeMapping.put("list", "array"); typeMapping.put("list", "array");
supportingFiles.add(new SupportingFile("composer.mustache", packagePath, "composer.json")); supportingFiles.add(new SupportingFile("composer.mustache", packagePath, "composer.json"));
supportingFiles.add(new SupportingFile("configuration.mustache", packagePath + "/lib", "Configuration.php"));
supportingFiles.add(new SupportingFile("APIClient.mustache", packagePath + "/lib", "APIClient.php")); supportingFiles.add(new SupportingFile("APIClient.mustache", packagePath + "/lib", "APIClient.php"));
supportingFiles.add(new SupportingFile("APIClientException.mustache", packagePath + "/lib", "APIClientException.php")); supportingFiles.add(new SupportingFile("APIClientException.mustache", packagePath + "/lib", "APIClientException.php"));
supportingFiles.add(new SupportingFile("require.mustache", packagePath, invokerPackage + ".php")); supportingFiles.add(new SupportingFile("require.mustache", packagePath, invokerPackage + ".php"));

View File

@ -34,13 +34,12 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
apiTemplateFiles.put("api.mustache", ".py"); apiTemplateFiles.put("api.mustache", ".py");
templateDir = "python"; templateDir = "python";
apiPackage = invokerPackage; apiPackage = invokerPackage + ".apis";
modelPackage = invokerPackage + ".models"; modelPackage = invokerPackage + ".models";
languageSpecificPrimitives.clear(); languageSpecificPrimitives.clear();
languageSpecificPrimitives.add("int"); languageSpecificPrimitives.add("int");
languageSpecificPrimitives.add("float"); languageSpecificPrimitives.add("float");
languageSpecificPrimitives.add("long");
languageSpecificPrimitives.add("list"); languageSpecificPrimitives.add("list");
languageSpecificPrimitives.add("bool"); languageSpecificPrimitives.add("bool");
languageSpecificPrimitives.add("str"); languageSpecificPrimitives.add("str");
@ -49,7 +48,7 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
typeMapping.clear(); typeMapping.clear();
typeMapping.put("integer", "int"); typeMapping.put("integer", "int");
typeMapping.put("float", "float"); typeMapping.put("float", "float");
typeMapping.put("long", "long"); typeMapping.put("long", "int");
typeMapping.put("double", "float"); typeMapping.put("double", "float");
typeMapping.put("array", "list"); typeMapping.put("array", "list");
typeMapping.put("map", "map"); typeMapping.put("map", "map");
@ -70,8 +69,11 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
supportingFiles.add(new SupportingFile("README.mustache", eggPackage, "README.md")); supportingFiles.add(new SupportingFile("README.mustache", eggPackage, "README.md"));
supportingFiles.add(new SupportingFile("setup.mustache", eggPackage, "setup.py")); supportingFiles.add(new SupportingFile("setup.mustache", eggPackage, "setup.py"));
supportingFiles.add(new SupportingFile("swagger.mustache", invokerPackage, "swagger.py")); supportingFiles.add(new SupportingFile("swagger.mustache", invokerPackage, "swagger.py"));
supportingFiles.add(new SupportingFile("rest.mustache", invokerPackage, "rest.py"));
supportingFiles.add(new SupportingFile("util.mustache", invokerPackage, "util.py"));
supportingFiles.add(new SupportingFile("__init__package.mustache", invokerPackage, "__init__.py")); supportingFiles.add(new SupportingFile("__init__package.mustache", invokerPackage, "__init__.py"));
supportingFiles.add(new SupportingFile("__init__model.mustache", modelPackage.replace('.', File.separatorChar), "__init__.py")); supportingFiles.add(new SupportingFile("__init__model.mustache", modelPackage.replace('.', File.separatorChar), "__init__.py"));
supportingFiles.add(new SupportingFile("__init__api.mustache", apiPackage.replace('.', File.separatorChar), "__init__.py"));
} }
@Override @Override
@ -113,9 +115,9 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
if(languageSpecificPrimitives.contains(type)) { if(languageSpecificPrimitives.contains(type)) {
return type; return type;
} }
} else {
type = toModelName(swaggerType);
} }
else
type = swaggerType;
return type; return type;
} }
@ -133,9 +135,9 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
if (name.matches("^[A-Z_]*$")) if (name.matches("^[A-Z_]*$"))
name = name.toLowerCase(); name = name.toLowerCase();
// camelize (lower first character) the variable name // underscore the variable name
// petId => pet_id // petId => pet_id
name = underscore(name); name = underscore(dropDots(name));
// for reserved word or word starting with number, append _ // for reserved word or word starting with number, append _
if(reservedWords.contains(name) || name.matches("^\\d.*")) if(reservedWords.contains(name) || name.matches("^\\d.*"))
@ -144,6 +146,10 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
return name; return name;
} }
private static String dropDots(String str) {
return str.replaceAll("\\.", "_");
}
@Override @Override
public String toParamName(String name) { public String toParamName(String name) {
// should be the same as variable name // should be the same as variable name
@ -168,8 +174,8 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
throw new RuntimeException(name + " (reserved word) cannot be used as a model name"); throw new RuntimeException(name + " (reserved word) cannot be used as a model name");
// underscore the model file name // underscore the model file name
// PhoneNumber.rb => phone_number.rb // PhoneNumber => phone_number
return underscore(name); return underscore(dropDots(name));
} }
@Override @Override

View File

@ -8,10 +8,9 @@ import java.util.*;
import java.io.File; import java.io.File;
public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig { public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
protected String invokerPackage = "com.wordnik.client"; protected String gemName = "swagger_client";
protected String groupId = "com.wordnik"; protected String moduleName = null;
protected String artifactId = "swagger-client"; protected String libFolder = "lib";
protected String artifactVersion = "1.0.0";
public CodegenType getTag() { public CodegenType getTag() {
return CodegenType.CLIENT; return CodegenType.CLIENT;
@ -25,10 +24,18 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
return "Generates a Ruby client library."; return "Generates a Ruby client library.";
} }
/**
* Generate Ruby module name from the gem name, e.g. use "SwaggerClient" for "swagger_client".
*/
public String generateModuleName() {
return camelize(gemName.replaceAll("[^\\w]+", "_"));
}
public RubyClientCodegen() { public RubyClientCodegen() {
super(); super();
modelPackage = "models"; moduleName = generateModuleName();
apiPackage = "lib"; modelPackage = gemName + "/models";
apiPackage = gemName + "/api";
outputFolder = "generated-code/ruby"; outputFolder = "generated-code/ruby";
modelTemplateFiles.put("model.mustache", ".rb"); modelTemplateFiles.put("model.mustache", ".rb");
apiTemplateFiles.put("api.mustache", ".rb"); apiTemplateFiles.put("api.mustache", ".rb");
@ -39,17 +46,15 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
reservedWords = new HashSet<String> ( reservedWords = new HashSet<String> (
Arrays.asList( Arrays.asList(
"__FILE__", "and", "def", "end", "in", "or", "self", "unless", "__LINE__", "__FILE__", "and", "def", "end", "in", "or", "self", "unless", "__LINE__",
"begin", "defined?", "ensure", "module", "redo", "super", "until", "BEGIN", "begin", "defined?", "ensure", "module", "redo", "super", "until", "BEGIN",
"break", "do", "false", "next", "rescue", "then", "when", "END", "case", "break", "do", "false", "next", "rescue", "then", "when", "END", "case",
"else", "for", "nil", "retry", "true", "while", "alias", "class", "elsif", "else", "for", "nil", "retry", "true", "while", "alias", "class", "elsif",
"if", "not", "return", "undef", "yield") "if", "not", "return", "undef", "yield")
); );
additionalProperties.put("invokerPackage", invokerPackage); additionalProperties.put("gemName", gemName);
additionalProperties.put("groupId", groupId); additionalProperties.put("moduleName", moduleName);
additionalProperties.put("artifactId", artifactId);
additionalProperties.put("artifactVersion", artifactVersion);
languageSpecificPrimitives.add("int"); languageSpecificPrimitives.add("int");
languageSpecificPrimitives.add("array"); languageSpecificPrimitives.add("array");
@ -64,15 +69,18 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
typeMapping.put("List", "array"); typeMapping.put("List", "array");
typeMapping.put("map", "map"); typeMapping.put("map", "map");
supportingFiles.add(new SupportingFile("swagger-client.gemspec.mustache", "", "swagger-client.gemspec")); String baseFolder = "lib/" + gemName;
supportingFiles.add(new SupportingFile("swagger-client.mustache", "", "lib/swagger-client.rb")); String swaggerFolder = baseFolder + "/swagger";
supportingFiles.add(new SupportingFile("swagger.mustache", "", "lib/swagger.rb")); String modelFolder = baseFolder + "/models";
supportingFiles.add(new SupportingFile("monkey.mustache", "", "lib/monkey.rb")); supportingFiles.add(new SupportingFile("swagger_client.gemspec.mustache", "", gemName + ".gemspec"));
supportingFiles.add(new SupportingFile("swagger/request.mustache", "", "lib/swagger/request.rb")); supportingFiles.add(new SupportingFile("swagger_client.mustache", "lib", gemName + ".rb"));
supportingFiles.add(new SupportingFile("swagger/response.mustache", "", "lib/swagger/response.rb")); supportingFiles.add(new SupportingFile("monkey.mustache", baseFolder, "monkey.rb"));
supportingFiles.add(new SupportingFile("swagger/version.mustache", "", "lib/swagger/version.rb")); supportingFiles.add(new SupportingFile("swagger.mustache", baseFolder, "swagger.rb"));
supportingFiles.add(new SupportingFile("swagger/configuration.mustache", "", "lib/swagger/configuration.rb")); supportingFiles.add(new SupportingFile("swagger/request.mustache", swaggerFolder, "request.rb"));
supportingFiles.add(new SupportingFile("base_object.mustache", "", "models/base_object.rb")); supportingFiles.add(new SupportingFile("swagger/response.mustache", swaggerFolder, "response.rb"));
supportingFiles.add(new SupportingFile("swagger/version.mustache", swaggerFolder, "version.rb"));
supportingFiles.add(new SupportingFile("swagger/configuration.mustache", swaggerFolder, "configuration.rb"));
supportingFiles.add(new SupportingFile("base_object.mustache", modelFolder, "base_object.rb"));
} }
@Override @Override
@ -82,11 +90,11 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
@Override @Override
public String apiFileFolder() { public String apiFileFolder() {
return outputFolder + "/" + apiPackage().replace('.', File.separatorChar); return outputFolder + File.separatorChar + "lib" + File.separatorChar + gemName + File.separatorChar + "api";
} }
public String modelFileFolder() { public String modelFileFolder() {
return outputFolder + "/" + modelPackage().replace('.', File.separatorChar); return outputFolder + File.separatorChar + "lib" + File.separatorChar + gemName + File.separatorChar + "models";
} }
@Override @Override
@ -150,13 +158,13 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
// should be the same as variable name // should be the same as variable name
return toVarName(name); return toVarName(name);
} }
@Override @Override
public String toModelName(String name) { public String toModelName(String name) {
// model name cannot use reserved keyword, e.g. return // model name cannot use reserved keyword, e.g. return
if(reservedWords.contains(name)) if(reservedWords.contains(name))
throw new RuntimeException(name + " (reserved word) cannot be used as a model name"); throw new RuntimeException(name + " (reserved word) cannot be used as a model name");
// camelize the model name // camelize the model name
// phone_number => PhoneNumber // phone_number => PhoneNumber
return camelize(name); return camelize(name);
@ -167,11 +175,11 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
// model name cannot use reserved keyword, e.g. return // model name cannot use reserved keyword, e.g. return
if(reservedWords.contains(name)) if(reservedWords.contains(name))
throw new RuntimeException(name + " (reserved word) cannot be used as a model name"); throw new RuntimeException(name + " (reserved word) cannot be used as a model name");
// underscore the model file name // underscore the model file name
// PhoneNumber.rb => phone_number.rb // PhoneNumber.rb => phone_number.rb
return underscore(name); return underscore(name);
} }
@Override @Override
public String toApiFilename(String name) { public String toApiFilename(String name) {
@ -186,7 +194,7 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
public String toApiName(String name) { public String toApiName(String name) {
if(name.length() == 0) if(name.length() == 0)
return "DefaultApi"; return "DefaultApi";
// e.g. phone_number_api => PhoneNumberApi // e.g. phone_number_api => PhoneNumberApi
return camelize(name) + "Api"; return camelize(name) + "Api";
} }
@ -196,8 +204,17 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
if(reservedWords.contains(operationId)) if(reservedWords.contains(operationId))
throw new RuntimeException(operationId + " (reserved word) cannot be used as method name"); throw new RuntimeException(operationId + " (reserved word) cannot be used as method name");
return underscore(operationId); return underscore(operationId);
} }
@Override
public String toModelImport(String name) {
return modelPackage() + "/" + toModelFilename(name);
}
@Override
public String toApiImport(String name) {
return apiPackage() + "/" + toApiFilename(name);
}
} }

View File

@ -1,6 +1,8 @@
package {{package}}; package {{package}};
import {{modelPackage}}.*; import {{modelPackage}}.*;
import {{package}}.{{classname}}Service;
import {{package}}.factories.{{classname}}ServiceFactory;
import com.wordnik.swagger.annotations.ApiParam; import com.wordnik.swagger.annotations.ApiParam;
@ -21,26 +23,31 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.*; import javax.ws.rs.*;
@Path("/{{baseName}}") @Path("/{{baseName}}")
{{#hasConsumes}}@Consumes({ {{#consumes}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}}
{{#hasProduces}}@Produces({ {{#produces}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}}
@com.wordnik.swagger.annotations.Api(value = "/{{baseName}}", description = "the {{baseName}} API") @com.wordnik.swagger.annotations.Api(value = "/{{baseName}}", description = "the {{baseName}} API")
{{#operations}} {{#operations}}
public class {{classname}} { public class {{classname}} {
{{#operation}}
@{{httpMethod}}
{{#subresourceOperation}}@Path("{{path}}"){{/subresourceOperation}}
{{#hasConsumes}}@Consumes({ {{#consumes}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}}
{{#hasProduces}}@Produces({ {{#produces}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}}
@com.wordnik.swagger.annotations.ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}})
@com.wordnik.swagger.annotations.ApiResponses(value = { {{#responses}}
@com.wordnik.swagger.annotations.ApiResponse(code = {{{code}}}, message = "{{{message}}}"){{#hasMore}},
{{/hasMore}}{{/responses}} })
public Response {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}}, private final {{classname}}Service delegate = {{classname}}ServiceFactory.get{{classname}}();
{{#operation}}
@{{httpMethod}}
{{#subresourceOperation}}@Path("{{path}}"){{/subresourceOperation}}
{{#hasConsumes}}@Consumes({ {{#consumes}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}}
{{#hasProduces}}@Produces({ {{#produces}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}}
@com.wordnik.swagger.annotations.ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}})
@com.wordnik.swagger.annotations.ApiResponses(value = { {{#responses}}
@com.wordnik.swagger.annotations.ApiResponse(code = {{{code}}}, message = "{{{message}}}"){{#hasMore}},
{{/hasMore}}{{/responses}} })
public Response {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},
{{/hasMore}}{{/allParams}}) {{/hasMore}}{{/allParams}})
throws NotFoundException { throws NotFoundException {
// do some magic! // do some magic!
return Response.ok().entity(new ApiResponseMessage(ApiResponseMessage.OK, "magic!")).build(); return delegate.{{nickname}}({{#allParams}}{{#isFile}}fileDetail{{/isFile}}{{^isFile}}{{paramName}}{{/isFile}}{{#hasMore}},{{/hasMore}}{{/allParams}});
} }
{{/operation}}
{{/operation}}
} }
{{/operations}} {{/operations}}

View File

@ -0,0 +1,28 @@
package {{package}};
import {{package}}.*;
import {{modelPackage}}.*;
import com.sun.jersey.multipart.FormDataParam;
{{#imports}}import {{import}};
{{/imports}}
import java.util.List;
import {{package}}.NotFoundException;
import java.io.InputStream;
import com.sun.jersey.core.header.FormDataContentDisposition;
import com.sun.jersey.multipart.FormDataParam;
import javax.ws.rs.core.Response;
{{#operations}}
public abstract class {{classname}}Service {
{{#operation}}
public abstract Response {{nickname}}({{#allParams}}{{>serviceQueryParams}}{{>servicePathParams}}{{>serviceHeaderParams}}{{>serviceBodyParams}}{{>serviceFormParams}}{{#hasMore}},{{/hasMore}}{{/allParams}})
throws NotFoundException;
{{/operation}}
}
{{/operations}}

View File

@ -0,0 +1,14 @@
package {{package}}.factories;
import {{package}}.{{classname}}Service;
import {{package}}.impl.{{classname}}ServiceImpl;
public class {{classname}}ServiceFactory {
private final static {{classname}}Service service = new {{classname}}ServiceImpl();
public static {{classname}}Service get{{classname}}()
{
return service;
}
}

View File

@ -0,0 +1,32 @@
package {{package}}.impl;
import {{package}}.*;
import {{modelPackage}}.*;
import com.sun.jersey.multipart.FormDataParam;
{{#imports}}import {{import}};
{{/imports}}
import java.util.List;
import {{package}}.NotFoundException;
import java.io.InputStream;
import com.sun.jersey.core.header.FormDataContentDisposition;
import com.sun.jersey.multipart.FormDataParam;
import javax.ws.rs.core.Response;
{{#operations}}
public class {{classname}}ServiceImpl extends {{classname}}Service {
{{#operation}}
@Override
public Response {{nickname}}({{#allParams}}{{>serviceQueryParams}}{{>servicePathParams}}{{>serviceHeaderParams}}{{>serviceBodyParams}}{{>serviceFormParams}}{{#hasMore}},{{/hasMore}}{{/allParams}})
throws NotFoundException {
// do some magic!
return Response.ok().entity(new ApiResponseMessage(ApiResponseMessage.OK, "magic!")).build();
}
{{/operation}}
}
{{/operations}}

View File

@ -62,6 +62,25 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.9.1</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/gen/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
<dependencies> <dependencies>
@ -129,7 +148,7 @@
</repository> </repository>
</repositories> </repositories>
<properties> <properties>
<swagger-core-version>1.5.2-M2-SNAPSHOT</swagger-core-version> <swagger-core-version>1.5.2-M2</swagger-core-version>
<jetty-version>9.2.9.v20150224</jetty-version> <jetty-version>9.2.9.v20150224</jetty-version>
<jersey-version>1.13</jersey-version> <jersey-version>1.13</jersey-version>
<slf4j-version>1.6.3</slf4j-version> <slf4j-version>1.6.3</slf4j-version>

View File

@ -0,0 +1 @@
{{#isBodyParam}}{{{dataType}}} {{paramName}}{{/isBodyParam}}

View File

@ -0,0 +1 @@
{{#isFormParam}}{{#notFile}}{{{dataType}}} {{paramName}}{{/notFile}}{{#isFile}}FormDataContentDisposition fileDetail{{/isFile}}{{/isFormParam}}

View File

@ -0,0 +1 @@
{{#isHeaderParam}}{{{dataType}}} {{paramName}}{{/isHeaderParam}}

View File

@ -0,0 +1 @@
{{#isPathParam}}{{{dataType}}} {{paramName}}{{/isPathParam}}

View File

@ -0,0 +1 @@
{{#isQueryParam}}{{{dataType}}} {{paramName}}{{/isQueryParam}}

View File

@ -1,2 +1,2 @@
{{#isFormParam}}{{#notFile}} {{#isFormParam}}{{#notFile}}
@ApiParam(value = "{{{description}}}"{{#required}}, required=true{{/required}} {{#allowableValues}}, allowableValues="{{{allowableValues}}}"{{/allowableValues}}{{#defaultValue}}, defaultValue="{{{defaultValue}}}"{{/defaultValue}})@RequestPart("{{paramName}}") {{{dataType}}} {{paramName}}{{/notFile}}{{#isFile}}@ApiParam(value = "file detail") @RequestPart("file") MultipartFile fileDetail{{/isFile}}{{/isFormParam}} @ApiParam(value = "{{{description}}}"{{#required}}, required=true{{/required}} {{#allowableValues}}, allowableValues="{{{allowableValues}}}"{{/allowableValues}}{{#defaultValue}}, defaultValue="{{{defaultValue}}}"{{/defaultValue}}) @RequestPart(value="{{paramName}}"{{#required}}, required=true{{/required}}{{^required}}, required=false{{/required}}) {{{dataType}}} {{paramName}}{{/notFile}}{{#isFile}}@ApiParam(value = "file detail") @RequestPart("file") MultipartFile fileDetail{{/isFile}}{{/isFormParam}}

View File

@ -1 +1 @@
{{#isHeaderParam}}@ApiParam(value = "{{{description}}}" {{#required}},required=true{{/required}} {{#allowableValues}}, allowableValues="{{{allowableValues}}}"{{/allowableValues}}{{#defaultValue}}, defaultValue="{{{defaultValue}}}"{{/defaultValue}})@RequestHeader("{{paramName}}") {{{dataType}}} {{paramName}}{{/isHeaderParam}} {{#isHeaderParam}}@ApiParam(value = "{{{description}}}" {{#required}},required=true{{/required}} {{#allowableValues}}, allowableValues="{{{allowableValues}}}"{{/allowableValues}}{{#defaultValue}}, defaultValue="{{{defaultValue}}}"{{/defaultValue}}) @RequestHeader(value="{{paramName}}", required={{#required}}true{{/required}}{{^required}}false{{/required}}) {{{dataType}}} {{paramName}}{{/isHeaderParam}}

View File

@ -1 +1 @@
{{#isQueryParam}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}, allowableValues = "{{{allowableValues}}}"{{/allowableValues}}{{#defaultValue}}, defaultValue = "{{{defaultValue}}}"{{/defaultValue}}) @RequestParam(value = "{{paramName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}) {{{dataType}}} {{paramName}}{{/isQueryParam}} {{#isQueryParam}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}, allowableValues = "{{{allowableValues}}}"{{/allowableValues}}{{#defaultValue}}, defaultValue = "{{{defaultValue}}}"{{/defaultValue}}) @RequestParam(value = "{{paramName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}{{#defaultValue}}, defaultValue="{{{defaultValue}}}"{{/defaultValue}}) {{{dataType}}} {{paramName}}{{/isQueryParam}}

View File

@ -19,3 +19,4 @@ com.wordnik.swagger.codegen.languages.StaticHtmlGenerator
com.wordnik.swagger.codegen.languages.SwaggerGenerator com.wordnik.swagger.codegen.languages.SwaggerGenerator
com.wordnik.swagger.codegen.languages.SwaggerYamlGenerator com.wordnik.swagger.codegen.languages.SwaggerYamlGenerator
com.wordnik.swagger.codegen.languages.TizenClientCodegen com.wordnik.swagger.codegen.languages.TizenClientCodegen
com.wordnik.swagger.codegen.languages.AkkaScalaClientCodegen

View File

@ -0,0 +1,43 @@
package {{package}}
{{#imports}}
import {{import}}
{{/imports}}
import {{invokerPackage}}._
import {{invokerPackage}}.CollectionFormats._
import {{invokerPackage}}.ApiKeyLocations._
{{#operations}}
object {{classname}} {
{{#operation}}
{{#javadocRenderer}}
{{>javadoc}}
{{/javadocRenderer}}
def {{operationId}}({{>methodParameters}}): ApiRequest[{{>operationReturnType}}] =
ApiRequest[{{>operationReturnType}}](ApiMethods.{{httpMethod.toUpperCase}}, "{{basePath}}", "{{path}}", {{#consumes.0}}"{{mediaType}}"{{/consumes.0}}{{^consumes}}"application/json"{{/consumes}})
{{#authMethods}}{{#isApiKey}}.withApiKey(apiKey, "{{keyParamName}}", {{#isKeyInQuery}}QUERY{{/isKeyInQuery}}{{#isKeyInHeader}}HEADER{{/isKeyInHeader}})
{{/isApiKey}}{{#isBasic}}.withCredentials(basicAuth)
{{/isBasic}}{{/authMethods}}{{#bodyParam}}.withBody({{paramName}})
{{/bodyParam}}{{#formParams}}.withFormParam({{>paramCreation}})
{{/formParams}}{{#queryParams}}.withQueryParam({{>paramCreation}})
{{/queryParams}}{{#pathParams}}.withPathParam({{>paramCreation}})
{{/pathParams}}{{#headerParams}}.withHeaderParam({{>paramCreation}})
{{/headerParams}}{{#responses}}{{^isWildcard}}{{#dataType}}.with{{>responseState}}Response[{{dataType}}]({{code}})
{{/dataType}}{{^dataType}}.with{{>responseState}}Response[Unit]({{code}})
{{/dataType}}{{/isWildcard}}{{/responses}}{{#responses}}{{#isWildcard}}{{#dataType}}.withDefault{{>responseState}}Response[{{dataType}}]
{{/dataType}}{{^dataType}}.withDefault{{>responseState}}Response[Unit]
{{/dataType}}{{/isWildcard}}{{/responses}}{{^responseHeaders.isEmpty}}
object {{#fnCapitalize}}{{operationId}}{{/fnCapitalize}}Headers { {{#responseHeaders}}
def {{name}}(r: ApiReturnWithHeaders) = r.get{{^isContainer}}{{baseType}}{{/isContainer}}{{#isContainer}}String{{/isContainer}}Header("{{baseName}}"){{/responseHeaders}}
}
{{/responseHeaders.isEmpty}}
{{/operation}}
{{#unknownStatusCodes}}
ApiInvoker.addCustomStatusCode({{value}}, isSuccess = false)
{{/unknownStatusCodes}}
}
{{/operations}}

View File

@ -0,0 +1,323 @@
package {{invokerPackage}}
import java.io.File
import java.security.cert.X509Certificate
import javax.net.ssl._
import akka.actor.ActorSystem
import akka.io.IO
import akka.pattern.ask
import akka.util.Timeout
import org.joda.time.DateTime
import org.joda.time.format.ISODateTimeFormat
import org.json4s.JsonAST.JString
import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.json4s.jackson.Serialization
import spray.can.Http
import spray.can.Http.HostConnectorSetup
import spray.client.pipelining
import spray.client.pipelining._
import spray.http.HttpEncodings._
import spray.http.HttpHeaders.{RawHeader, `Accept-Encoding`}
import spray.http.Uri.Query
import spray.http._
import spray.http.parser.HttpParser
import spray.httpx.encoding.{Deflate, Encoder, Gzip}
import spray.httpx.unmarshalling._
import spray.io.ClientSSLEngineProvider
import scala.concurrent.{ExecutionContext, Future}
import scala.reflect.ClassTag
import scala.util.control.NonFatal
object ApiInvoker {
def apply()(implicit system: ActorSystem): ApiInvoker =
apply(DefaultFormats + DateTimeSerializer)
def apply(serializers: Traversable[Serializer[_]])(implicit system: ActorSystem): ApiInvoker =
apply(DefaultFormats + DateTimeSerializer ++ serializers)
def apply(formats: Formats)(implicit system: ActorSystem): ApiInvoker = new ApiInvoker(formats)
case class CustomStatusCode(value: Int, reason: String = "Application-defined status code", isSuccess: Boolean = true)
def addCustomStatusCode(code: CustomStatusCode): Unit = addCustomStatusCode(code.value, code.reason, code.isSuccess)
def addCustomStatusCode(code: Int, reason: String = "Application defined code", isSuccess: Boolean = true) = {
StatusCodes.getForKey(code) foreach { c =>
StatusCodes.registerCustom(code, reason, reason, isSuccess, allowsEntity = true)
}
}
/**
* Allows request execution without calling apiInvoker.execute(request)
* request.response can be used to get a future of the ApiResponse generated.
* request.result can be used to get a future of the expected ApiResponse content. If content doesn't match, a
* Future will failed with a ClassCastException
* @param request the apiRequest to be executed
*/
implicit class ApiRequestImprovements[T](request: ApiRequest[T]) {
def response(invoker: ApiInvoker)(implicit ec: ExecutionContext, system: ActorSystem): Future[ApiResponse[T]] =
response(ec, system, invoker)
def response(implicit ec: ExecutionContext, system: ActorSystem, invoker: ApiInvoker): Future[ApiResponse[T]] =
invoker.execute(request)
def result[U <: T](implicit c: ClassTag[U], ec: ExecutionContext, system: ActorSystem, invoker: ApiInvoker): Future[U] =
invoker.execute(request).map(_.content).mapTo[U]
}
/**
* Allows transformation from ApiMethod to spray HttpMethods
* @param method the ApiMethod to be converted
*/
implicit class ApiMethodExtensions(val method: ApiMethod) {
def toSprayMethod: HttpMethod = HttpMethods.getForKey(method.value).getOrElse(HttpMethods.GET)
}
case object DateTimeSerializer extends CustomSerializer[DateTime](format => ( {
case JString(s) =>
ISODateTimeFormat.dateTimeParser().parseDateTime(s)
}, {
case d: DateTime =>
JString(ISODateTimeFormat.dateTimeParser().print(d))
}))
}
class ApiInvoker(formats: Formats)(implicit system: ActorSystem) extends UntrustedSslContext with CustomContentTypes {
import io.swagger.client.core.ApiInvoker._
import io.swagger.client.core.ParametersMap._
implicit val ec = system.dispatcher
implicit val jsonFormats = formats
def settings = ApiSettings(system)
import spray.http.MessagePredicate._
val CompressionFilter = MessagePredicate({ _ => settings.compressionEnabled}) &&
Encoder.DefaultFilter &&
minEntitySize(settings.compressionSizeThreshold)
settings.customCodes.foreach(addCustomStatusCode)
private def addAuthentication(credentialsSeq: Seq[Credentials]): pipelining.RequestTransformer =
request =>
credentialsSeq.foldLeft(request) {
case (req, BasicCredentials(login, password)) =>
req ~> addCredentials(BasicHttpCredentials(login, password))
case (req, ApiKeyCredentials(keyValue, keyName, ApiKeyLocations.HEADER)) =>
req ~> addHeader(RawHeader(keyName, keyValue.value))
case (req, _) => req
}
private def addHeaders(headers: Map[String, Any]): pipelining.RequestTransformer = { request =>
val rawHeaders = for {
(name, value) <- headers.asFormattedParams
header = RawHeader(name, String.valueOf(value))
} yield header
request.withHeaders(rawHeaders.toList)
}
private def bodyPart(name: String, value: Any): BodyPart = {
value match {
case f: File =>
BodyPart(f, name)
case v: String =>
BodyPart(HttpEntity(String.valueOf(v)))
case NumericValue(v) =>
BodyPart(HttpEntity(String.valueOf(v)))
case m: ApiModel =>
BodyPart(HttpEntity(Serialization.write(m)))
}
}
private def formDataContent(request: ApiRequest[_]) = {
val params = request.formParams.asFormattedParams
if (params.isEmpty)
None
else
Some(
normalizedContentType(request.contentType).mediaType match {
case MediaTypes.`multipart/form-data` =>
MultipartFormData(params.map { case (name, value) => (name, bodyPart(name, value))})
case MediaTypes.`application/x-www-form-urlencoded` =>
FormData(params.mapValues(String.valueOf))
case m: MediaType => // Default : application/x-www-form-urlencoded.
FormData(params.mapValues(String.valueOf))
}
)
}
private def bodyContent(request: ApiRequest[_]): Option[Any] = {
request.bodyParam.map(Extraction.decompose).map(compact)
}
private def createRequest(uri: Uri, request: ApiRequest[_]): HttpRequest = {
val builder = new RequestBuilder(request.method.toSprayMethod)
val httpRequest = request.method.toSprayMethod match {
case HttpMethods.GET | HttpMethods.DELETE => builder.apply(uri)
case HttpMethods.POST | HttpMethods.PUT =>
formDataContent(request) orElse bodyContent(request) match {
case Some(c: FormData) =>
builder.apply(uri, c)
case Some(c: MultipartFormData) =>
builder.apply(uri, c)
case Some(c: String) =>
builder.apply(uri, HttpEntity(normalizedContentType(request.contentType), c))
case _ =>
builder.apply(uri, HttpEntity(normalizedContentType(request.contentType), " "))
}
case _ => builder.apply(uri)
}
httpRequest ~>
addHeaders(request.headerParams) ~>
addAuthentication(request.credentials) ~>
encode(Gzip(CompressionFilter))
}
def makeQuery(r: ApiRequest[_]): Query = {
r.credentials.foldLeft(r.queryParams) {
case (params, ApiKeyCredentials(key, keyName, ApiKeyLocations.QUERY)) =>
params + (keyName -> key.value)
case (params, _) => params
}.asFormattedParams
.mapValues(String.valueOf)
.foldRight[Query](Uri.Query.Empty) {
case ((name, value), acc) => acc.+:(name, value)
}
}
def makeUri(r: ApiRequest[_]): Uri = {
val opPath = r.operationPath.replaceAll("\\{format\\}", "json")
val opPathWithParams = r.pathParams.asFormattedParams
.mapValues(String.valueOf)
.foldLeft(opPath) {
case (path, (name, value)) => path.replaceAll(s"\\{$name\\}", value)
}
val query = makeQuery(r)
Uri(r.basePath + opPathWithParams).withQuery(query)
}
def execute[T](r: ApiRequest[T]): Future[ApiResponse[T]] = {
try {
implicit val timeout: Timeout = settings.connectionTimeout
val uri = makeUri(r)
val connector = HostConnectorSetup(
uri.authority.host.toString,
uri.effectivePort,
sslEncryption = "https".equals(uri.scheme),
defaultHeaders = settings.defaultHeaders ++ List(`Accept-Encoding`(gzip, deflate)))
val request = createRequest(uri, r)
for {
Http.HostConnectorInfo(hostConnector, _) <- IO(Http) ? connector
response <- hostConnector.ask(request).mapTo[HttpResponse]
} yield {
response ~> decode(Deflate) ~> decode(Gzip) ~> unmarshallApiResponse(r)
}
}
catch {
case NonFatal(x) => Future.failed(x)
}
}
def unmarshallApiResponse[T](request: ApiRequest[T])(response: HttpResponse): ApiResponse[T] = {
request.responseForCode(response.status.intValue) match {
case Some( (manifest: Manifest[T], state: ResponseState) ) =>
entityUnmarshaller(manifest)(response.entity) match {
case Right(value) ⇒
state match {
case ResponseState.Success =>
ApiResponse(response.status.intValue, value, response.headers.map(header => (header.name, header.value)).toMap)
case ResponseState.Error =>
throw new ApiError(response.status.intValue, "Error response received",
Some(value),
headers = response.headers.map(header => (header.name, header.value)).toMap)
}
case Left(MalformedContent(error, Some(cause))) ⇒
throw new ApiError(response.status.intValue, s"Unable to unmarshall content to [$manifest]", Some(response.entity.toString), cause)
case Left(MalformedContent(error, None)) ⇒
throw new ApiError(response.status.intValue, s"Unable to unmarshall content to [$manifest]", Some(response.entity.toString))
case Left(ContentExpected) ⇒
throw new ApiError(response.status.intValue, s"Unable to unmarshall empty response to [$manifest]", Some(response.entity.toString))
}
case _ => throw new ApiError(response.status.intValue, "Unexpected response code", Some(response.entity.toString))
}
}
def entityUnmarshaller[T](implicit mf: Manifest[T]): Unmarshaller[T] =
Unmarshaller[T](MediaTypes.`application/json`) {
case x: HttpEntity.NonEmpty ⇒
parse(x.asString(defaultCharset = HttpCharsets.`UTF-8`))
.noNulls
.camelizeKeys
.extract[T]
}
}
sealed trait CustomContentTypes {
def normalizedContentType(original: String): ContentType =
MediaTypes.forExtension(original) map (ContentType(_)) getOrElse parseContentType(original)
def parseContentType(contentType: String): ContentType = {
val contentTypeAsRawHeader = HttpHeaders.RawHeader("Content-Type", contentType)
val parsedContentTypeHeader = HttpParser.parseHeader(contentTypeAsRawHeader)
(parsedContentTypeHeader: @unchecked) match {
case Right(ct: HttpHeaders.`Content-Type`) =>
ct.contentType
case Left(error: ErrorInfo) =>
throw new IllegalArgumentException(
s"Error converting '$contentType' to a ContentType header: '${error.summary}'")
}
}
}
sealed trait UntrustedSslContext {
this: ApiInvoker =>
implicit lazy val trustfulSslContext: SSLContext = {
settings.alwaysTrustCertificates match {
case false =>
SSLContext.getDefault
case true =>
class IgnoreX509TrustManager extends X509TrustManager {
def checkClientTrusted(chain: Array[X509Certificate], authType: String): Unit = {}
def checkServerTrusted(chain: Array[X509Certificate], authType: String): Unit = {}
def getAcceptedIssuers = null
}
val context = SSLContext.getInstance("TLS")
context.init(null, Array(new IgnoreX509TrustManager), null)
context
}
}
implicit val clientSSLEngineProvider =
ClientSSLEngineProvider {
_ =>
val engine = trustfulSslContext.createSSLEngine()
engine.setUseClientMode(true)
engine
}
}

View File

@ -0,0 +1,50 @@
package {{invokerPackage}}
sealed trait ResponseState
object ResponseState {
case object Success extends ResponseState
case object Error extends ResponseState
}
case class ApiRequest[U](
// required fields
method: ApiMethod,
basePath: String,
operationPath: String,
contentType: String,
// optional fields
responses: Map[Int, (Manifest[_], ResponseState)] = Map.empty,
bodyParam: Option[Any] = None,
formParams: Map[String, Any] = Map.empty,
pathParams: Map[String, Any] = Map.empty,
queryParams: Map[String, Any] = Map.empty,
headerParams: Map[String, Any] = Map.empty,
credentials: Seq[Credentials] = List.empty) {
def withCredentials(cred: Credentials) = copy[U](credentials = credentials :+ cred)
def withApiKey(key: ApiKeyValue, keyName: String, location: ApiKeyLocation) = withCredentials(ApiKeyCredentials(key, keyName, location))
def withSuccessResponse[T](code: Int)(implicit m: Manifest[T]) = copy[U](responses = responses + (code -> (m, ResponseState.Success)))
def withErrorResponse[T](code: Int)(implicit m: Manifest[T]) = copy[U](responses = responses + (code -> (m, ResponseState.Error)))
def withDefaultSuccessResponse[T](implicit m: Manifest[T]) = withSuccessResponse[T](0)
def withDefaultErrorResponse[T](implicit m: Manifest[T]) = withErrorResponse[T](0)
def responseForCode(statusCode: Int): Option[(Manifest[_], ResponseState)] = responses.get(statusCode) orElse responses.get(0)
def withoutBody() = copy[U](bodyParam = None)
def withBody(body: Any) = copy[U](bodyParam = Some(body))
def withFormParam(name: String, value: Any) = copy[U](formParams = formParams + (name -> value))
def withPathParam(name: String, value: Any) = copy[U](pathParams = pathParams + (name -> value))
def withQueryParam(name: String, value: Any) = copy[U](queryParams = queryParams + (name -> value))
def withHeaderParam(name: String, value: Any) = copy[U](headerParams = headerParams + (name -> value))
}

View File

@ -0,0 +1,32 @@
package {{invokerPackage}}
import java.util.concurrent.TimeUnit
import akka.actor.{ExtendedActorSystem, Extension, ExtensionKey}
import com.typesafe.config.Config
import io.swagger.client.core.ApiInvoker.CustomStatusCode
import spray.http.HttpHeaders.RawHeader
import scala.collection.JavaConversions._
import scala.concurrent.duration.FiniteDuration
class ApiSettings(config: Config) extends Extension {
def this(system: ExtendedActorSystem) = this(system.settings.config)
private def cfg = config.getConfig("io.swagger.client.apiRequest")
val alwaysTrustCertificates = cfg.getBoolean("trust-certificates")
val defaultHeaders = cfg.getConfig("default-headers").entrySet.toList.map(c => RawHeader(c.getKey, c.getValue.render))
val connectionTimeout = FiniteDuration(cfg.getDuration("connection-timeout", TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS)
val compressionEnabled = cfg.getBoolean("compression.enabled")
val compressionSizeThreshold = cfg.getBytes("compression.size-threshold").toInt
val customCodes = cfg.getConfigList("custom-codes").toList.map { c => CustomStatusCode(
c.getInt("code"),
c.getString("reason"),
c.getBoolean("success"))
}
}
object ApiSettings extends ExtensionKey[ApiSettings]

View File

@ -0,0 +1,42 @@
package {{apiPackage}}
import {{modelPackage}}._
import org.json4s._
import scala.reflect.ClassTag
object EnumsSerializers {
def all = Seq[Serializer[_]](){{#models}}{{#model}}{{#hasEnums}}{{#vars}}{{#isEnum}} :+
new EnumNameSerializer({{classname}}Enums.{{datatypeWithEnum}}){{/isEnum}}{{/vars}}{{/hasEnums}}{{/model}}{{/models}}
private class EnumNameSerializer[E <: Enumeration: ClassTag](enum: E)
extends Serializer[E#Value] {
import JsonDSL._
val EnumerationClass = classOf[E#Value]
def deserialize(implicit format: Formats):
PartialFunction[(TypeInfo, JValue), E#Value] = {
case (t @ TypeInfo(EnumerationClass, _), json) if isValid(json) => {
json match {
case JString(value) =>
enum.withName(value)
case value =>
throw new MappingException(s"Can't convert $value to $EnumerationClass")
}
}
}
private[this] def isValid(json: JValue) = json match {
case JString(value) if enum.values.exists(_.toString == value) => true
case _ => false
}
def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
case i: E#Value => i.toString
}
}
}

View File

@ -0,0 +1,21 @@
{{^notes.isEmpty}}
{{notes}}
{{/notes.isEmpty}}
Expected answers:
{{#responses}}
code {{code}} : {{dataType}} {{^message.isEmpty}}({{message}}){{/message.isEmpty}}{{^headers.isEmpty}}
Headers :{{#headers}}
{{baseName}} - {{description}}{{/headers}}{{/headers.isEmpty}}
{{/responses}}
{{#authMethods.0}}
Available security schemes:
{{#authMethods}}
{{name}} ({{type}})
{{/authMethods}}
{{/authMethods.0}}
{{#allParams}}
@param {{paramName}} {{description}}
{{/allParams}}

View File

@ -0,0 +1 @@
{{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}{{#isContainer}}{{dataType}}{{/isContainer}}{{^isContainer}}Option[{{dataType}}]{{/isContainer}}{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}{{^required}}{{^isContainer}} = None{{/isContainer}}{{/required}}{{/defaultValue}}{{#hasMore}}, {{/hasMore}}{{/allParams}}{{#authMethods.0}})(implicit {{#authMethods}}{{#isApiKey}}apiKey: ApiKeyValue{{/isApiKey}}{{#isBasic}}basicAuth: BasicCredentials{{/isBasic}}{{#hasMore}}, {{/hasMore}}{{/authMethods}}{{/authMethods.0}}

View File

@ -0,0 +1,30 @@
package {{package}}
import {{invokerPackage}}.ApiModel
import org.joda.time.DateTime
{{#models}}
{{#model}}
case class {{classname}} (
{{#vars}}{{#description}}/* {{{description}}} */
{{/description}}{{name}}: {{^required}}Option[{{/required}}{{^isEnum}}{{datatype}}{{/isEnum}}{{#isEnum}}{{classname}}Enums.{{datatypeWithEnum}}{{/isEnum}}{{^required}}]{{/required}}{{#hasMore}},{{/hasMore}}{{^hasMore}}){{/hasMore}}
{{/vars}} extends ApiModel
{{#hasEnums}}
object {{classname}}Enums {
{{#vars}}{{#isEnum}}type {{datatypeWithEnum}} = {{datatypeWithEnum}}.Value
{{/isEnum}}{{/vars}}
{{#vars}}{{#isEnum}}object {{datatypeWithEnum}} extends Enumeration {
{{#_enum}}
val {{#fnEnumEntry}}{{.}}{{/fnEnumEntry}} = Value("{{.}}")
{{/_enum}}
}
{{/isEnum}}{{/vars}}
}
{{/hasEnums}}
{{/model}}
{{/models}}

View File

@ -0,0 +1 @@
{{#onlyOneSuccess}}{{^defaultResponse}}Unit{{/defaultResponse}}{{#defaultResponse}}{{#responses}}{{#isDefault}}{{#dataType}}{{dataType}}{{/dataType}}{{^dataType}}Unit{{/dataType}}{{/isDefault}}{{/responses}}{{/defaultResponse}}{{/onlyOneSuccess}}{{^onlyOneSuccess}}{{#responses}}{{#-first}}{{^hasMore}}{{#dataType}}{{dataType}}{{/dataType}}{{^dataType}}Unit{{/dataType}}{{/hasMore}}{{#hasMore}}Any{{/hasMore}}{{/-first}}{{/responses}}{{/onlyOneSuccess}}

View File

@ -0,0 +1 @@
"{{baseName}}", {{#isContainer}}ArrayValues({{paramName}}{{#collectionFormat}}, {{collectionFormat.toUpperCase}}{{/collectionFormat}}){{/isContainer}}{{^isContainer}}{{paramName}}{{/isContainer}}

View File

@ -0,0 +1,227 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>{{groupId}}</groupId>
<artifactId>{{artifactId}}</artifactId>
<packaging>jar</packaging>
<name>{{artifactId}}</name>
<version>{{artifactVersion}}</version>
<prerequisites>
<maven>2.2.0</maven>
</prerequisites>
<pluginRepositories>
<pluginRepository>
<id>maven-mongodb-plugin-repo</id>
<name>maven mongodb plugin repository</name>
<url>http://maven-mongodb-plugin.googlecode.com/svn/maven/repo</url>
<layout>default</layout>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<configuration>
<systemProperties>
<property>
<name>loggerPath</name>
<value>conf/log4j.properties</value>
</property>
</systemProperties>
<argLine>-Xms512m -Xmx1500m</argLine>
<parallel>methods</parallel>
<forkMode>pertest</forkMode>
</configuration>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!-- attach test jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add_sources</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>
src/main/java</source>
</sources>
</configuration>
</execution>
<execution>
<id>add_test_sources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>
src/test/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>
1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>${scala-maven-plugin-version}</version>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>scala-test-compile</id>
<phase>process-test-resources</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<args>
<arg>-feature</arg>
</args>
<jvmArgs>
<jvmArg>-Xms128m</jvmArg>
<jvmArg>-Xmx1500m</jvmArg>
</jvmArgs>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<configuration>
<scalaVersion>${scala-version}</scalaVersion>
</configuration>
</plugin>
</plugins>
</reporting>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala-version}</version>
</dependency>
<dependency>
<groupId>com.wordnik</groupId>
<artifactId>swagger-core</artifactId>
<version>${swagger-core-version}</version>
</dependency>
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest_2.10</artifactId>
<version>${scala-test-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${joda-time-version}</version>
</dependency>
<dependency>
<groupId>org.joda</groupId>
<artifactId>joda-convert</artifactId>
<version>${joda-version}</version>
</dependency>
<dependency>
<groupId>com.typesafe</groupId>
<artifactId>config</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.10</artifactId>
<version>${akka-version}</version>
</dependency>
<dependency>
<groupId>io.spray</groupId>
<artifactId>spray-client</artifactId>
<version>${spray-version}</version>
</dependency>
<dependency>
<groupId>org.json4s</groupId>
<artifactId>json4s-jackson_2.10</artifactId>
<version>${json4s-jackson-version}</version>
</dependency>
</dependencies>
<properties>
<scala-version>2.10.4</scala-version>
<json4s-jackson-version>3.2.11</json4s-jackson-version>
<json4s-ext-version>3.2.11</json4s-ext-version>
<spray-version>1.3.1</spray-version>
<akka-version>2.3.9</akka-version>
<joda-version>1.2</joda-version>
<joda-time-version>2.2</joda-time-version>
<swagger-core-version>1.5.0-M1</swagger-core-version>
<maven-plugin.version>1.0.0</maven-plugin.version>
<junit-version>4.8.1</junit-version>
<scala-maven-plugin-version>3.1.5</scala-maven-plugin-version>
<scala-test-version>2.1.3</scala-test-version>
</properties>
</project>

View File

@ -0,0 +1,24 @@
{{configKeyPath}} {
{{configKey}} {
compression {
enabled: false
size-threshold: 0
}
trust-certificates: true
connection-timeout: {{defaultTimeout}}ms
default-headers {
"userAgent": "{{artifactId}}_{{artifactVersion}}"
}
// let you define custom http status code, as in :
// { code: 601, reason: "some custom http status code", success: false }
custom-codes : []
}
}
spray.can.host-connector.max-redirects = 10

View File

@ -0,0 +1,166 @@
package {{invokerPackage}}
import java.io.File
import java.net.URLEncoder
import scala.util.Try
sealed trait ApiReturnWithHeaders {
def headers: Map[String, String]
def header(name: String): Option[String] = headers.get(name)
def getStringHeader(name: String) = header(name)
def getIntHeader(name: String) = castedHeader(name, java.lang.Integer.parseInt)
def getLongHeader(name: String) = castedHeader(name, java.lang.Long.parseLong)
def getFloatHeader(name: String) = castedHeader(name, java.lang.Float.parseFloat)
def getDoubleHeader(name: String) = castedHeader(name, java.lang.Double.parseDouble)
def getBooleanHeader(name: String) = castedHeader(name, java.lang.Boolean.parseBoolean)
private def castedHeader[U](name: String, conversion: String => U): Option[U] = { Try { header(name).map( conversion ) }.get }
}
sealed case class ApiResponse[T](code: Int, content: T, headers: Map[String, String] = Map.empty)
extends ApiReturnWithHeaders
sealed case class ApiError[T](code: Int, message: String, responseContent: Option[T], cause: Throwable = null, headers: Map[String, String] = Map.empty)
extends Throwable(s"($code) $message.${responseContent.map(s => s" Content : $s").getOrElse("")}", cause)
with ApiReturnWithHeaders
sealed case class ApiMethod(value: String)
object ApiMethods {
val CONNECT = ApiMethod("CONNECT")
val DELETE = ApiMethod("DELETE")
val GET = ApiMethod("GET")
val HEAD = ApiMethod("HEAD")
val OPTIONS = ApiMethod("OPTIONS")
val PATCH = ApiMethod("PATCH")
val POST = ApiMethod("POST")
val PUT = ApiMethod("PUT")
val TRACE = ApiMethod("TRACE")
}
/**
* This trait needs to be added to any model defined by the api.
*/
trait ApiModel
/**
* Single trait defining a credential that can be transformed to a paramName / paramValue tupple
*/
sealed trait Credentials {
def asQueryParam: Option[(String, String)] = None
}
sealed case class BasicCredentials(user: String, password: String) extends Credentials
sealed case class ApiKeyCredentials(key: ApiKeyValue, keyName: String, location: ApiKeyLocation) extends Credentials {
override def asQueryParam: Option[(String, String)] = location match {
case ApiKeyLocations.QUERY => Some((keyName, key.value))
case _ => None
}
}
sealed case class ApiKeyValue(value: String)
sealed trait ApiKeyLocation
object ApiKeyLocations {
case object QUERY extends ApiKeyLocation
case object HEADER extends ApiKeyLocation
}
/**
* Case class used to unapply numeric values only in pattern matching
* @param value the string representation of the numeric value
*/
sealed case class NumericValue(value: String) {
override def toString = value
}
object NumericValue {
def unapply(n: Any): Option[NumericValue] = n match {
case (_: Int | _: Long | _: Float | _: Double | _: Boolean | _: Byte) => Some(NumericValue(String.valueOf(n)))
case _ => None
}
}
/**
* Used for params being arrays
*/
sealed case class ArrayValues(values: Seq[Any], format: CollectionFormat = CollectionFormats.CSV)
object ArrayValues {
def apply(values: Option[Seq[Any]], format: CollectionFormat): ArrayValues =
ArrayValues(values.getOrElse(Seq.empty), format)
def apply(values: Option[Seq[Any]]): ArrayValues = ArrayValues(values, CollectionFormats.CSV)
}
/**
* Defines how arrays should be rendered in query strings.
*/
sealed trait CollectionFormat
trait MergedArrayFormat extends CollectionFormat {
def separator: String
}
object CollectionFormats {
case object CSV extends MergedArrayFormat {
override val separator = ","
}
case object TSV extends MergedArrayFormat {
override val separator = "\t"
}
case object SSV extends MergedArrayFormat {
override val separator = " "
}
case object PIPES extends MergedArrayFormat {
override val separator = "|"
}
case object MULTI extends CollectionFormat
}
object ParametersMap {
/**
* Pimp parameters maps (Map[String, Any]) in order to transform them in a sequence of String -> Any tupples,
* with valid url-encoding, arrays handling, files preservation, ...
*/
implicit class ParametersMapImprovements(val m: Map[String, Any]) {
def asFormattedParamsList = m.toList.flatMap(formattedParams)
def asFormattedParams = m.flatMap(formattedParams)
private def urlEncode(v: Any) = URLEncoder.encode(String.valueOf(v), "utf-8").replaceAll("\\+", "%20")
private def formattedParams(tuple: (String, Any)): Seq[(String, Any)] = formattedParams(tuple._1, tuple._2)
private def formattedParams(name: String, value: Any): Seq[(String, Any)] = value match {
case arr: ArrayValues =>
arr.format match {
case CollectionFormats.MULTI => arr.values.flatMap(formattedParams(name, _))
case format: MergedArrayFormat => Seq((name, arr.values.mkString(format.separator)))
}
case None => Seq.empty
case Some(opt) =>
formattedParams(name, opt)
case s: Seq[Any] =>
formattedParams(name, ArrayValues(s))
case v: String => Seq((name, urlEncode(v)))
case NumericValue(v) => Seq((name, urlEncode(v)))
case f: File => Seq((name, f))
case m: ApiModel => Seq((name, m))
}
}
}

View File

@ -0,0 +1 @@
{{#onlyOneSuccess}}{{#isDefault}}Success{{/isDefault}}{{^isDefault}}Error{{/isDefault}}{{/onlyOneSuccess}}{{^onlyOneSuccess}}Success{{/onlyOneSuccess}}

View File

@ -1,9 +1,17 @@
{{#useAndroidMavenGradlePlugin}}
group = '{{groupId}}'
project.version = '{{artifactVersion}}'
{{/useAndroidMavenGradlePlugin}}
buildscript { buildscript {
repositories { repositories {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:1.2.2' classpath 'com.android.tools.build:gradle:1.2.2'
{{#useAndroidMavenGradlePlugin}}
classpath 'com.github.dcendents:android-maven-plugin:1.2'
{{/useAndroidMavenGradlePlugin}}
} }
} }
@ -13,7 +21,11 @@ allprojects {
} }
} }
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
{{#useAndroidMavenGradlePlugin}}
apply plugin: 'com.github.dcendents.android-maven'
{{/useAndroidMavenGradlePlugin}}
android { android {
compileSdkVersion 22 compileSdkVersion 22
@ -22,6 +34,17 @@ android {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 22 targetSdkVersion 22
} }
// Rename the aar correctly
libraryVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.aar')) {
def fileName = "${project.name}-${variant.baseName}-${version}.aar"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
} }
@ -37,7 +60,12 @@ dependencies {
compile "com.google.code.gson:gson:$gson_version" compile "com.google.code.gson:gson:$gson_version"
compile "org.apache.httpcomponents:httpcore:$httpclient_version" compile "org.apache.httpcomponents:httpcore:$httpclient_version"
compile "org.apache.httpcomponents:httpclient:$httpclient_version" compile "org.apache.httpcomponents:httpclient:$httpclient_version"
compile "org.apache.httpcomponents:httpmime:$httpclient_version" compile ("org.apache.httpcomponents:httpcore:$httpcore_version") {
exclude(group: 'org.apache.httpcomponents', module: 'httpclient')
}
compile ("org.apache.httpcomponents:httpmime:$httpmime_version") {
exclude(group: 'org.apache.httpcomponents', module: 'httpclient')
}
testCompile "junit:junit:$junit_version" testCompile "junit:junit:$junit_version"
} }
@ -48,7 +76,18 @@ afterEvaluate {
task.dependsOn variant.javaCompile task.dependsOn variant.javaCompile
task.from variant.javaCompile.destinationDir task.from variant.javaCompile.destinationDir
task.destinationDir = project.file("${project.buildDir}/outputs/jar") task.destinationDir = project.file("${project.buildDir}/outputs/jar")
task.archiveName = "${project.name}-${variant.baseName}.jar" task.archiveName = "${project.name}-${variant.baseName}-${version}.jar"
artifacts.add('archives', task); artifacts.add('archives', task);
} }
} }
{{#useAndroidMavenGradlePlugin}}
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
artifacts {
archives sourcesJar
}
{{/useAndroidMavenGradlePlugin}}

View File

@ -0,0 +1 @@
rootProject.name = "{{artifactId}}"

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using RestSharp;
using {{invokerPackage}}; using {{invokerPackage}};
using {{modelPackage}}; using {{modelPackage}};
{{#imports}} {{#imports}}
@ -9,101 +10,73 @@ namespace {{package}} {
{{#operations}} {{#operations}}
public class {{classname}} { public class {{classname}} {
string basePath; string basePath;
private readonly ApiInvoker apiInvoker = ApiInvoker.GetInstance(); protected RestClient restClient;
public {{classname}}(String basePath = "{{basePath}}") public {{classname}}(String basePath = "{{basePath}}")
{ {
this.basePath = basePath; this.basePath = basePath;
this.restClient = new RestClient(basePath);
} }
public ApiInvoker getInvoker() { /// <summary>
return apiInvoker; /// Sets the endpoint base url for the services being accessed
} /// </summary>
/// <param name="basePath"> Base URL
// Sets the endpoint base url for the services being accessed /// <returns></returns>
public void setBasePath(string basePath) { public void SetBasePath(string basePath) {
this.basePath = basePath; this.basePath = basePath;
} }
// Gets the endpoint base url for the services being accessed /// <summary>
public String getBasePath() { /// Gets the endpoint base url for the services being accessed
return basePath; /// <returns>Base URL</returns>
/// </summary>
public String GetBasePath() {
return this.basePath;
} }
{{#operation}} {{#operation}}
/// <summary> /// <summary>
/// {{summary}} {{notes}} /// {{summary}} {{notes}}
/// </summary> /// </summary>
{{#allParams}}/// <param name="{{paramName}}">{{description}}</param> {{#allParams}} /// <param name="{{paramName}}">{{description}}</param>
{{#hasMore}} {{/hasMore}}{{/allParams}} {{/allParams}}
/// <returns></returns> /// <returns>{{#returnType}}{{{returnType}}}{{/returnType}}</returns>
public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}} {{nickname}} ({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) { public {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{nickname}} ({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) {
// create path and map variables
var path = "{{path}}".Replace("{format}","json"){{#pathParams}}.Replace("{" + "{{baseName}}" + "}", apiInvoker.ParameterToString({{{paramName}}})){{/pathParams}};
// query params var _request = new RestRequest("{{path}}", Method.{{httpMethod}});
var queryParams = new Dictionary<String, String>();
var headerParams = new Dictionary<String, String>();
var formParams = new Dictionary<String, object>();
{{#requiredParamCount}} {{#allParams}}{{#required}}
// verify required params are set // verify the required parameter '{{paramName}}' is set
if ({{/requiredParamCount}}{{#requiredParams}} {{paramName}} == null {{#hasMore}}|| {{/hasMore}}{{/requiredParams}}{{#requiredParamCount}}) { if ({{paramName}} == null) throw new ApiException(400, "Missing required parameter '{{paramName}}' when calling {{nickname}}");
throw new ApiException(400, "missing required params"); {{/required}}{{/allParams}}
// add default header, if any
foreach(KeyValuePair<string, string> defaultHeader in ApiInvoker.GetDefaultHeader())
{
_request.AddHeader(defaultHeader.Key, defaultHeader.Value);
} }
{{/requiredParamCount}}
{{#queryParams}}if ({{paramName}} != null){ _request.AddUrlSegment("format", "json"); // set format to json by default
queryParams.Add("{{baseName}}", apiInvoker.ParameterToString({{paramName}})); {{#pathParams}}_request.AddUrlSegment("{{baseName}}", ApiInvoker.ParameterToString({{{paramName}}})); // path (url segment) parameter
} {{/pathParams}}
{{#queryParams}} if ({{paramName}} != null) _request.AddParameter("{{baseName}}", ApiInvoker.ParameterToString({{paramName}})); // query parameter
{{/queryParams}} {{/queryParams}}
{{#headerParams}} if ({{paramName}} != null) _request.AddHeader("{{baseName}}", ApiInvoker.ParameterToString({{paramName}})); // header parameter
{{#headerParams}}headerParams.Add("{{baseName}}", apiInvoker.ParameterToString({{paramName}}));
{{/headerParams}} {{/headerParams}}
{{#formParams}}if ({{paramName}} != null) {{#isFile}}_request.AddFile("{{baseName}}", {{paramName}});{{/isFile}}{{^isFile}}_request.AddParameter("{{baseName}}", ApiInvoker.ParameterToString({{paramName}})); // form parameter{{/isFile}}
{{#formParams}}if ({{paramName}} != null){
if({{paramName}} is byte[]) {
formParams.Add("{{baseName}}", {{paramName}});
} else {
formParams.Add("{{baseName}}", apiInvoker.ParameterToString({{paramName}}));
}
}
{{/formParams}} {{/formParams}}
{{#bodyParam}}_request.AddParameter("application/json", ApiInvoker.Serialize({{paramName}}), ParameterType.RequestBody); // http body (model) parameter
{{/bodyParam}}
try { // make the HTTP request
if (typeof({{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}) == typeof(byte[])) { IRestResponse response = restClient.Execute(_request);
{{#returnType}} if (((int)response.StatusCode) >= 400) {
var response = apiInvoker.invokeBinaryAPI(basePath, path, "GET", queryParams, null, headerParams, formParams); throw new ApiException ((int)response.StatusCode, "Error calling {{nickname}}: " + response.Content);
return ((object)response) as {{{returnType}}};
{{/returnType}}
{{^returnType}}
apiInvoker.invokeBinaryAPI(basePath, path, "GET", queryParams, null, headerParams, formParams);
return;
{{/returnType}}
} else {
{{#returnType}}
var response = apiInvoker.invokeAPI(basePath, path, "{{httpMethod}}", queryParams, {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}, headerParams, formParams);
if (response != null){
return ({{{returnType}}}) ApiInvoker.deserialize(response, typeof({{{returnType}}}));
}
else {
return null;
}
{{/returnType}}
{{^returnType}}
apiInvoker.invokeAPI(basePath, path, "{{httpMethod}}", queryParams, {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}, headerParams, formParams);
return;
{{/returnType}}
}
} catch (ApiException ex) {
if(ex.ErrorCode == 404) {
return {{#returnType}}null{{/returnType}};
}
else {
throw ex;
}
} }
{{#returnType}}return ({{{returnType}}}) ApiInvoker.Deserialize(response.Content, typeof({{{returnType}}}));{{/returnType}}{{^returnType}}
return;{{/returnType}}
} }
{{/operation}} {{/operation}}
} }

View File

@ -1,21 +1,17 @@
using System; using System;
namespace {{invokerPackage}} { namespace {{invokerPackage}} {
public class ApiException : Exception { public class ApiException : Exception {
private int errorCode = 0; public int ErrorCode { get; set; }
public ApiException() {} public ApiException() {}
public int ErrorCode { public ApiException(int errorCode, string message) : base(message) {
get this.ErrorCode = errorCode;
{
return errorCode;
}
} }
public ApiException(int errorCode, string message) : base(message) {
this.errorCode = errorCode;
}
} }
}
}

View File

@ -1,235 +1,81 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text; using System.Text;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace {{invokerPackage}} { namespace {{invokerPackage}} {
public class ApiInvoker { public class ApiInvoker {
private static readonly ApiInvoker _instance = new ApiInvoker(); private static Dictionary<String, String> defaultHeaderMap = new Dictionary<String, String>();
private Dictionary<String, String> defaultHeaderMap = new Dictionary<String, String>();
public static ApiInvoker GetInstance() { /// <summary>
return _instance; /// Add default header
} /// </summary>
/// <param name="key"> Header field name
/// <param name="value"> Header field value
/// <returns></returns>
public static void AddDefaultHeader(string key, string value) {
defaultHeaderMap.Add(key, value);
}
/// <summary> /// <summary>
/// Add default header /// Get default header
/// </summary> /// </summary>
/// <param name="key"> Header field name /// <returns>Dictionary of default header</returns>
/// <param name="value"> Header field value public static Dictionary<String, String> GetDefaultHeader() {
/// <returns></returns> return defaultHeaderMap;
public void addDefaultHeader(string key, string value) { }
defaultHeaderMap.Add(key, value);
}
/// <summary> /// <summary>
/// escape string (url-encoded) /// escape string (url-encoded)
/// </summary> /// </summary>
/// <param name="str"> String to be escaped /// <param name="str"> String to be escaped
/// <returns>Escaped string</returns> /// <returns>Escaped string</returns>
public string escapeString(string str) { public static string EscapeString(string str) {
return str; return str;
} }
/// <summary> /// <summary>
/// if parameter is DateTime, output in ISO8601 format, otherwise just return the string /// if parameter is DateTime, output in ISO8601 format, otherwise just return the string
/// </summary> /// </summary>
/// <param name="obj"> The parameter (header, path, query, form) /// <param name="obj"> The parameter (header, path, query, form)
/// <returns>Formatted string</returns> /// <returns>Formatted string</returns>
public string ParameterToString(object obj) public static string ParameterToString(object obj)
{
return (obj is DateTime) ? ((DateTime)obj).ToString ("u") : Convert.ToString (obj);
}
/// <summary>
/// Deserialize the JSON string into a proper object
/// </summary>
/// <param name="json"> JSON string
/// <param name="type"> Object type
/// <returns>Object representation of the JSON string</returns>
public static object Deserialize(string json, Type type) {
try
{ {
return (obj is DateTime) ? ((DateTime)obj).ToString ("u") : Convert.ToString (obj); return JsonConvert.DeserializeObject(json, type);
} }
catch (IOException e) {
/// <summary> throw new ApiException(500, e.Message);
/// Deserialize the JSON string into a proper object
/// </summary>
/// <param name="json"> JSON string
/// <param name="type"> Object type
/// <returns>Object representation of the JSON string</returns>
public static object deserialize(string json, Type type) {
try
{
return JsonConvert.DeserializeObject(json, type);
}
catch (IOException e) {
throw new ApiException(500, e.Message);
}
} }
}
public static string serialize(object obj) { /// <summary>
try /// Serialize an object into JSON string
{ /// </summary>
return obj != null ? JsonConvert.SerializeObject(obj) : null; /// <param name="obj"> Object
} /// <returns>JSON string</returns>
catch (Exception e) { public static string Serialize(object obj) {
throw new ApiException(500, e.Message); try
}
}
public string invokeAPI(string host, string path, string method, Dictionary<String, String> queryParams, object body, Dictionary<String, String> headerParams, Dictionary<String, object> formParams)
{ {
return invokeAPIInternal(host, path, method, false, queryParams, body, headerParams, formParams) as string; return obj != null ? JsonConvert.SerializeObject(obj) : null;
} }
catch (Exception e) {
public byte[] invokeBinaryAPI(string host, string path, string method, Dictionary<String, String> queryParams, object body, Dictionary<String, String> headerParams, Dictionary<String, object> formParams) throw new ApiException(500, e.Message);
{
return invokeAPIInternal(host, path, method, true, queryParams, body, headerParams, formParams) as byte[];
}
private object invokeAPIInternal(string host, string path, string method, bool binaryResponse, Dictionary<String, String> queryParams, object body, Dictionary<String, String> headerParams, Dictionary<String, object> formParams) {
var b = new StringBuilder();
foreach (var queryParamItem in queryParams)
{
var value = queryParamItem.Value;
if (value == null) continue;
b.Append(b.ToString().Length == 0 ? "?" : "&");
b.Append(escapeString(queryParamItem.Key)).Append("=").Append(escapeString(value));
}
var querystring = b.ToString();
host = host.EndsWith("/") ? host.Substring(0, host.Length - 1) : host;
var client = WebRequest.Create(host + path + querystring);
client.Method = method;
byte[] formData = null;
if (formParams.Count > 0)
{
string formDataBoundary = String.Format("----------{0:N}", Guid.NewGuid());
client.ContentType = "multipart/form-data; boundary=" + formDataBoundary;
formData = GetMultipartFormData(formParams, formDataBoundary);
client.ContentLength = formData.Length;
}
else
{
client.ContentType = "application/json";
}
foreach (var headerParamsItem in headerParams)
{
client.Headers.Add(headerParamsItem.Key, headerParamsItem.Value);
}
foreach (var defaultHeaderMapItem in defaultHeaderMap.Where(defaultHeaderMapItem => !headerParams.ContainsKey(defaultHeaderMapItem.Key)))
{
client.Headers.Add(defaultHeaderMapItem.Key, defaultHeaderMapItem.Value);
}
switch (method)
{
case "GET":
break;
case "POST":
case "PATCH":
case "PUT":
case "DELETE":
using (Stream requestStream = client.GetRequestStream())
{
if (formData != null)
{
requestStream.Write(formData, 0, formData.Length);
}
var swRequestWriter = new StreamWriter(requestStream);
swRequestWriter.Write(serialize(body));
swRequestWriter.Close();
}
break;
default:
throw new ApiException(500, "unknown method type " + method);
}
try
{
var webResponse = (HttpWebResponse)client.GetResponse();
if (webResponse.StatusCode != HttpStatusCode.OK)
{
webResponse.Close();
throw new ApiException((int)webResponse.StatusCode, webResponse.StatusDescription);
}
if (binaryResponse)
{
using (var memoryStream = new MemoryStream())
{
webResponse.GetResponseStream().CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
else
{
using (var responseReader = new StreamReader(webResponse.GetResponseStream()))
{
var responseData = responseReader.ReadToEnd();
return responseData;
}
}
}
catch(WebException ex)
{
var response = ex.Response as HttpWebResponse;
int statusCode = 0;
if (response != null)
{
statusCode = (int)response.StatusCode;
response.Close();
}
throw new ApiException(statusCode, ex.Message);
}
}
private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary)
{
Stream formDataStream = new System.IO.MemoryStream();
bool needsCLRF = false;
foreach (var param in postParameters)
{
// Thanks to feedback from commenters, add a CRLF to allow multiple parameters to be added.
// Skip it on the first parameter, add it to subsequent parameters.
if (needsCLRF)
formDataStream.Write(Encoding.UTF8.GetBytes("\r\n"), 0, Encoding.UTF8.GetByteCount("\r\n"));
needsCLRF = true;
if (param.Value is byte[])
{
string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n",
boundary,
param.Key,
"application/octet-stream");
formDataStream.Write(Encoding.UTF8.GetBytes(postData), 0, Encoding.UTF8.GetByteCount(postData));
// Write the file data directly to the Stream, rather than serializing it to a string.
formDataStream.Write((param.Value as byte[]), 0, (param.Value as byte[]).Length);
}
else
{
string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}",
boundary,
param.Key,
param.Value);
formDataStream.Write(Encoding.UTF8.GetBytes(postData), 0, Encoding.UTF8.GetByteCount(postData));
}
}
// Add the end of the request. Start with a newline
string footer = "\r\n--" + boundary + "--\r\n";
formDataStream.Write(Encoding.UTF8.GetBytes(footer), 0, Encoding.UTF8.GetByteCount(footer));
// Dump the Stream into a byte[]
formDataStream.Position = 0;
byte[] formData = new byte[formDataStream.Length];
formDataStream.Read(formData, 0, formData.Length);
formDataStream.Close();
return formData;
} }
} }
} }
}

View File

@ -2,19 +2,19 @@ using System;
using System.Text; using System.Text;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.Serialization;
{{#models}} {{#models}}
{{#model}} {{#model}}
namespace {{package}} { namespace {{package}} {
[DataContract]
public class {{classname}} { public class {{classname}} {
{{#vars}} {{#vars}}
{{#description}}/* {{{description}}} */{{/description}}
{{#description}}/* {{{description}}} */ [DataMember(Name="{{baseName}}", EmitDefaultValue=false)]
{{/description}}
public {{{datatype}}} {{name}} { get; set; } public {{{datatype}}} {{name}} { get; set; }
{{/vars}} {{/vars}}
public override string ToString() { public override string ToString() {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append("class {{classname}} {\n"); sb.Append("class {{classname}} {\n");
@ -27,4 +27,4 @@ namespace {{package}} {
} }
{{/model}} {{/model}}
{{/models}} {{/models}}
} }

View File

@ -35,7 +35,7 @@
{{#examples}} {{#examples}}
<h3 class="field-label">Example data</h3> <h3 class="field-label">Example data</h3>
<div class="example-data-content-type">Content-Type: {{{contentType}}}</div> <div class="example-data-content-type">Content-Type: {{{contentType}}}</div>
<pre class="example"><code>{{{example}}}</code></pre> <pre class="example"><code>{{example}}</code></pre>
{{/examples}} {{/examples}}
</div> <!-- method --> </div> <!-- method -->
<hr> <hr>

View File

@ -0,0 +1,6 @@
#import <Foundation/Foundation.h>
#import <ISO8601/ISO8601.h>
#import <JSONModel/JSONValueTransformer.h>
@interface JSONValueTransformer (ISO8601)
@end

View File

@ -0,0 +1,10 @@
#import "JSONValueTransformer+ISO8601.h"
@implementation JSONValueTransformer (ISO8601)
- (NSDate *) NSDateFromNSString:(NSString *)string
{
return [NSDate dateWithISO8601String:string];
}
@end

View File

@ -2,3 +2,4 @@ platform :ios, '6.0'
xcodeproj '{{projectName}}/{{projectName}}.xcodeproj' xcodeproj '{{projectName}}/{{projectName}}.xcodeproj'
pod 'AFNetworking', '~> 2.1' pod 'AFNetworking', '~> 2.1'
pod 'JSONModel', '~> 1.0' pod 'JSONModel', '~> 1.0'
pod 'ISO8601'

View File

@ -1,12 +0,0 @@
#import <Foundation/Foundation.h>
#import "SWGObject.h"
@interface SWGDate : SWGObject {
@private
NSDate *_date;
}
@property(nonatomic, readonly) NSDate* date;
- (id) initWithValues: (NSString*)input;
-(NSString*) toString;
@end

View File

@ -1,34 +0,0 @@
#import "SWGDate.h"
@implementation SWGDate
@synthesize date = _date;
- (id) initWithValues:(NSString*)input {
if([input isKindOfClass:[NSString class]]){
NSDateFormatter* df = [NSDateFormatter new];
NSLocale *locale = [[NSLocale new]
initWithLocaleIdentifier:@"en_US_POSIX"];
[df setLocale:locale];
[df setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
_date = [df dateFromString:input];
}
else if([input isKindOfClass:[NSNumber class]]) {
NSTimeInterval interval = [input doubleValue];
_date = [[NSDate alloc] initWithTimeIntervalSince1970:interval];
}
return self;
}
-(NSString*) toString {
NSDateFormatter* df = [NSDateFormatter new];
NSLocale *locale = [[NSLocale new]
initWithLocaleIdentifier:@"en_US_POSIX"];
[df setLocale:locale];
[df setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
return [df stringFromDate:_date];
}
@end

View File

@ -69,6 +69,11 @@ static NSString * basePath = @"{{basePath}}";
{{#returnBaseType}}{{#hasParams}}completionHandler: {{/hasParams}}(void (^)({{{returnType}}} output, NSError* error))completionBlock{{/returnBaseType}} {{#returnBaseType}}{{#hasParams}}completionHandler: {{/hasParams}}(void (^)({{{returnType}}} output, NSError* error))completionBlock{{/returnBaseType}}
{{^returnBaseType}}{{#hasParams}}completionHandler: {{/hasParams}}(void (^)(NSError* error))completionBlock{{/returnBaseType}} { {{^returnBaseType}}{{#hasParams}}completionHandler: {{/hasParams}}(void (^)(NSError* error))completionBlock{{/returnBaseType}} {
{{#allParams}}{{#required}}
// verify the required parameter '{{paramName}}' is set
NSAssert({{paramName}} != nil, @"Missing the required parameter `{{paramName}}` when calling {{nickname}}");
{{/required}}{{/allParams}}
NSMutableString* requestUrl = [NSMutableString stringWithFormat:@"%@{{path}}", basePath]; NSMutableString* requestUrl = [NSMutableString stringWithFormat:@"%@{{path}}", basePath];
// remove format in URL if needed // remove format in URL if needed

View File

@ -24,6 +24,8 @@ class APIClient {
public static $GET = "GET"; public static $GET = "GET";
public static $PUT = "PUT"; public static $PUT = "PUT";
public static $DELETE = "DELETE"; public static $DELETE = "DELETE";
private static $default_header = array();
/* /*
* @var string timeout (second) of the HTTP request, by default set to 0, no timeout * @var string timeout (second) of the HTTP request, by default set to 0, no timeout
@ -36,37 +38,110 @@ class APIClient {
protected $user_agent = "PHP-Swagger"; protected $user_agent = "PHP-Swagger";
/** /**
* @param string $host the address of the API server * @param string $host Base url of the API server (optional)
* @param string $headerName a header to pass on requests
*/ */
function __construct($host, $headerName = null, $headerValue = null) { function __construct($host = null) {
$this->host = $host; if ($host == null) {
$this->headerName = $headerName; $this->host = '{{basePath}}';
$this->headerValue = $headerValue; } else {
$this->host = $host;
}
} }
/** /**
* Set the user agent of the API client * add default header
* *
* @param string $user_agent The user agent of the API client * @param string $header_name header name (e.g. Token)
* @param string $header_value header value (e.g. 1z8wp3)
*/
public function addDefaultHeader($header_name, $header_value) {
if (!is_string($header_name))
throw new \InvalidArgumentException('Header name must be a string.');
self::$default_header[$header_name] = $header_value;
}
/**
* get the default header
*
* @return array default header
*/
public function getDefaultHeader() {
return self::$default_header;
}
/**
* delete the default header based on header name
*
* @param string $header_name header name (e.g. Token)
*/
public function deleteDefaultHeader($header_name) {
unset(self::$default_header[$header_name]);
}
/**
* set the user agent of the api client
*
* @param string $user_agent the user agent of the api client
*/ */
public function setUserAgent($user_agent) { public function setUserAgent($user_agent) {
if (!is_string($user_agent)) { if (!is_string($user_agent))
throw new Exception('User-agent must be a string.'); throw new \InvalidArgumentException('User-agent must be a string.');
}
$this->user_agent= $user_agent; $this->user_agent= $user_agent;
} }
/** /**
* @param integer $seconds Number of seconds before timing out [set to 0 for no timeout] * @param integer $seconds Number of seconds before timing out [set to 0 for no timeout]
*/ */
public function setTimeout($seconds) { public function setTimeout($seconds) {
if (!is_numeric($seconds)) { if (!is_numeric($seconds))
throw new Exception('Timeout variable must be numeric.'); throw new \InvalidArgumentException('Timeout variable must be numeric.');
}
$this->curl_timeout = $seconds; $this->curl_timeout = $seconds;
} }
/**
* Get API key (with prefix if set)
* @param string key name
* @return string API key with the prefix
*/
public function getApiKeyWithPrefix($apiKey) {
if (Configuration::$apiKeyPrefix[$apiKey]) {
return Configuration::$apiKeyPrefix[$apiKey]." ".Configuration::$apiKey[$apiKey];
} else {
return Configuration::$apiKey[$apiKey];
}
}
/**
* update hearder and query param based on authentication setting
*
* @param array $headerParams header parameters (by ref)
* @param array $queryParams query parameters (by ref)
* @param array $authSettings array of authentication scheme (e.g ['api_key'])
*/
public function updateParamsForAuth(&$headerParams, &$queryParams, $authSettings)
{
if (count($authSettings) == 0)
return;
// one endpoint can have more than 1 auth settings
foreach($authSettings as $auth) {
// determine which one to use
switch($auth) {
{{#authMethods}}
case '{{name}}':
{{#isApiKey}}{{#isKeyInHeader}}$headerParams['{{keyParamName}}'] = $this->getApiKeyWithPrefix('{{keyParamName}}');{{/isKeyInHeader}}{{#isKeyInQuery}}$queryParams['{{keyParamName}}'] = $this->getApiKeyWithPrefix('{{keyParamName}}');{{/isKeyInQuery}}{{/isApiKey}}{{#isBasic}}$headerParams['Authorization'] = 'Basic '.base64_encode(Configuration::$username.":".Configuration::$password);{{/isBasic}}
{{#isOAuth}}//TODO support oauth{{/isOAuth}}
break;
{{/authMethods}}
default:
//TODO show warning about security definition not found
}
}
}
/** /**
* @param string $resourcePath path to method endpoint * @param string $resourcePath path to method endpoint
* @param string $method method to call * @param string $method method to call
@ -76,23 +151,22 @@ class APIClient {
* @return mixed * @return mixed
*/ */
public function callAPI($resourcePath, $method, $queryParams, $postData, public function callAPI($resourcePath, $method, $queryParams, $postData,
$headerParams) { $headerParams, $authSettings) {
$headers = array(); $headers = array();
# Allow API key from $headerParams to override default # determine authentication setting
$added_api_key = False; $this->updateParamsForAuth($headerParams, $queryParams, $authSettings);
# construct the http header
if ($headerParams != null) { if ($headerParams != null) {
# add default header
$headerParams = array_merge((array)self::$default_header, $headerParams);
foreach ($headerParams as $key => $val) { foreach ($headerParams as $key => $val) {
$headers[] = "$key: $val"; $headers[] = "$key: $val";
if ($key == $this->headerName) {
$added_api_key = True;
}
} }
} }
if (! $added_api_key && $this->headerName != null) {
$headers[] = $this->headerName . ": " . $this->headerValue;
}
// form data // form data
if ($postData and in_array('Content-Type: application/x-www-form-urlencoded', $headers)) { if ($postData and in_array('Content-Type: application/x-www-form-urlencoded', $headers)) {
@ -111,6 +185,7 @@ class APIClient {
} }
// return the result on success, rather than just TRUE // return the result on success, rather than just TRUE
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
if (! empty($queryParams)) { if (! empty($queryParams)) {
@ -130,7 +205,7 @@ class APIClient {
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($curl, CURLOPT_POSTFIELDS, $postData); curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
} else if ($method != self::$GET) { } else if ($method != self::$GET) {
throw new Exception('Method ' . $method . ' is not recognized.'); throw new APIClientException('Method ' . $method . ' is not recognized.');
} }
curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_URL, $url);
@ -261,7 +336,6 @@ class APIClient {
* @param string $class class name is passed as a string * @param string $class class name is passed as a string
* @return object an instance of $class * @return object an instance of $class
*/ */
public static function deserialize($data, $class) public static function deserialize($data, $class)
{ {
if (null === $data) { if (null === $data) {
@ -304,5 +378,37 @@ class APIClient {
return $deserialized; return $deserialized;
} }
/*
* return the header 'Accept' based on an array of Accept provided
*
* @param array[string] $accept Array of header
* @return string Accept (e.g. application/json)
*/
public static function selectHeaderAccept($accept) {
if (count($accept) === 0 or (count($accept) === 1 and $accept[0] === '')) {
return NULL;
} elseif (preg_grep("/application\/json/i", $accept)) {
return 'application/json';
} else {
return implode(',', $accept);
}
}
/*
* return the content type based on an array of content-type provided
*
* @param array[string] content_type_array Array fo content-type
* @return string Content-Type (e.g. application/json)
*/
public static function selectHeaderContentType($content_type) {
if (count($content_type) === 0 or (count($content_type) === 1 and $content_type[0] === '')) {
return 'application/json';
} elseif (preg_grep("/application\/json/i", $content_type)) {
return 'application/json';
} else {
return implode(',', $content_type);
}
}
} }

View File

@ -42,7 +42,7 @@ class {{classname}} {
{{#allParams}}{{#required}} {{#allParams}}{{#required}}
// verify the required parameter '{{paramName}}' is set // verify the required parameter '{{paramName}}' is set
if (${{paramName}} === null) { if (${{paramName}} === null) {
throw new \Exception("Missing the required parameter ${{paramName}} when calling {{nickname}}"); throw new \InvalidArgumentException('Missing the required parameter ${{paramName}} when calling {{nickname}}');
} }
{{/required}}{{/allParams}} {{/required}}{{/allParams}}
@ -54,12 +54,11 @@ class {{classname}} {
$queryParams = array(); $queryParams = array();
$headerParams = array(); $headerParams = array();
$formParams = array(); $formParams = array();
$_header_accept = '{{#produces}}{{mediaType}}{{#hasMore}}, {{/hasMore}}{{/produces}}'; $_header_accept = $this->apiClient->selectHeaderAccept(array({{#produces}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/produces}}));
if ($_header_accept !== '') { if (!is_null($_header_accept)) {
$headerParams['Accept'] = $_header_accept; $headerParams['Accept'] = $_header_accept;
} }
$_header_content_type = array({{#consumes}}'{{mediaType}}'{{#hasMore}},{{/hasMore}}{{/consumes}}); $headerParams['Content-Type'] = $this->apiClient->selectHeaderContentType(array({{#consumes}}'{{mediaType}}'{{#hasMore}},{{/hasMore}}{{/consumes}}));
$headerParams['Content-Type'] = count($_header_content_type) > 0 ? $_header_content_type[0] : 'application/json';
{{#queryParams}}// query params {{#queryParams}}// query params
if(${{paramName}} !== null) { if(${{paramName}} !== null) {
@ -92,18 +91,20 @@ class {{classname}} {
$httpBody = $formParams; $httpBody = $formParams;
} }
// authentication setting, if any
$authSettings = array({{#authMethods}}'{{name}}'{{#hasMore}}, {{/hasMore}}{{/authMethods}});
// make the API Call // make the API Call
$response = $this->apiClient->callAPI($resourcePath, $method, $response = $this->apiClient->callAPI($resourcePath, $method,
$queryParams, $httpBody, $queryParams, $httpBody,
$headerParams); $headerParams, $authSettings);
{{#returnType}}if(! $response) { {{#returnType}}if(! $response) {
return null; return null;
} }
$responseObject = $this->apiClient->deserialize($response, $responseObject = $this->apiClient->deserialize($response,'{{returnType}}');
'{{returnType}}'); return $responseObject;{{/returnType}}
return $responseObject;{{/returnType}}
} }
{{/operation}} {{/operation}}
{{newline}} {{newline}}

View File

@ -0,0 +1,36 @@
<?php
/**
* Copyright 2015 Reverb Technologies, Inc.
*
* Licensed 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.
*/
namespace {{invokerPackage}};
class Configuration {
public static $PATCH = "PATCH";
public static $POST = "POST";
public static $GET = "GET";
public static $PUT = "PUT";
public static $DELETE = "DELETE";
// authentication setting
public static $apiKey = array();
public static $apiKeyPrefix = array();
public static $username = '';
public static $password = '';
}

View File

@ -38,5 +38,37 @@ TODO
## Tests ## Tests
TODO (Make sure you are running it inside of a [virtualenv](http://docs.python-guide.org/en/latest/dev/virtualenvs/))
You can run the tests in the current python platform:
```sh
$ make test
[... magically installs dependencies and runs tests on your virtualenv]
Ran 7 tests in 19.289s
OK
```
or
```
$ mvn integration-test -rf :PythonPetstoreClientTests
Using 2195432783 as seed
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 37.594 s
[INFO] Finished at: 2015-05-16T18:00:35+08:00
[INFO] Final Memory: 11M/156M
[INFO] ------------------------------------------------------------------------
```
If you want to run the tests in all the python platforms:
```sh
$ make test-all
[... tox creates a virtualenv for every platform and runs tests inside of each]
py27: commands succeeded
py34: commands succeeded
congratulations :)
```

View File

@ -0,0 +1,5 @@
from __future__ import absolute_import
# import apis into api package
{{#apiInfo}}{{#apis}}from .{{classVarName}} import {{classname}}
{{/apis}}{{/apiInfo}}

View File

@ -1,13 +1,5 @@
#!/usr/bin/env python from __future__ import absolute_import
"""Add all of the modules in the current directory to __all__"""
import os
{{#models}}{{#model}} # import models into model package
from .{{classVarName}} import {{classname}} {{#models}}{{#model}}from .{{classVarName}} import {{classname}}
{{/model}}{{/models}} {{/model}}{{/models}}
__all__ = []
for module in os.listdir(os.path.dirname(__file__)):
if module != '__init__.py' and module[-3:] == '.py':
__all__.append(module[:-3])

View File

@ -1,22 +1,10 @@
#!/usr/bin/env python from __future__ import absolute_import
"""Add all of the modules in the current directory to __all__"""
import os
# import models into package # import models into sdk package
{{#models}}{{#model}} {{#models}}{{#model}}from .models.{{classVarName}} import {{classname}}
from .models.{{classVarName}} import {{classname}}
{{/model}}{{/models}} {{/model}}{{/models}}
# import apis into sdk package
# import apis into package {{#apiInfo}}{{#apis}}from .apis.{{classVarName}} import {{classname}}
{{#apiInfo}}{{#apis}}
from .{{classVarName}} import {{classname}}
{{/apis}}{{/apiInfo}} {{/apis}}{{/apiInfo}}
# import ApiClient # import ApiClient
from .swagger import ApiClient from .swagger import ApiClient
__all__ = []
for module in os.listdir(os.path.dirname(__file__)):
if module != '__init__.py' and module[-3:] == '.py':
__all__.append(module[:-3])

View File

@ -19,100 +19,74 @@ Copyright 2015 Reverb Technologies, Inc.
NOTE: This class is auto generated by the swagger code generator program. Do not edit the class manually. NOTE: This class is auto generated by the swagger code generator program. Do not edit the class manually.
""" """
from __future__ import absolute_import
import sys import sys
import os import os
import urllib
from models import * # python 2 and python 3 compatibility library
from six import iteritems
from ..util import remove_none
{{#operations}} {{#operations}}
class {{classname}}(object): class {{classname}}(object):
def __init__(self, apiClient): def __init__(self, api_client):
self.apiClient = apiClient self.api_client = api_client
{{newline}}
{{#operation}} {{#operation}}
def {{nickname}}(self, {{#requiredParams}}{{paramName}}{{#defaultValue}} = None{{/defaultValue}}, {{/requiredParams}}**kwargs): def {{nickname}}(self, {{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}**kwargs):
"""{{{summary}}} """
{{{summary}}}
{{{notes}}} {{{notes}}}
Args: {{#allParams}}:param {{dataType}} {{paramName}}: {{{description}}} {{#required}}(required){{/required}}{{#optional}}(optional){{/optional}}
{{#allParams}}{{paramName}}, {{dataType}}: {{{description}}} {{^optional}}(required){{/optional}}{{#optional}}(optional){{/optional}} {{/allParams}}
{{/allParams}} :return: {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}None{{/returnType}}
Returns: {{returnType}}
""" """
{{#allParams}}{{#required}}
allParams = [{{#allParams}}'{{paramName}}'{{#hasMore}}, {{/hasMore}}{{/allParams}}] # verify the required parameter '{{paramName}}' is set
if {{paramName}} is None:
raise ValueError("Missing the required parameter `{{paramName}}` when calling `{{nickname}}`")
{{/required}}{{/allParams}}
all_params = [{{#allParams}}'{{paramName}}'{{#hasMore}}, {{/hasMore}}{{/allParams}}]
params = locals() params = locals()
for (key, val) in params['kwargs'].iteritems(): for key, val in iteritems(params['kwargs']):
if key not in allParams: if key not in all_params:
raise TypeError("Got an unexpected keyword argument '%s' to method {{nickname}}" % key) raise TypeError("Got an unexpected keyword argument '%s' to method {{nickname}}" % key)
params[key] = val params[key] = val
del params['kwargs'] del params['kwargs']
resourcePath = '{{path}}' resource_path = '{{path}}'.replace('{format}', 'json')
resourcePath = resourcePath.replace('{format}', 'json')
method = '{{httpMethod}}' method = '{{httpMethod}}'
queryParams = {} path_params = remove_none(dict({{#pathParams}}{{baseName}}=params.get('{{paramName}}'){{#hasMore}}, {{/hasMore}}{{/pathParams}}))
headerParams = {} query_params = remove_none(dict({{#queryParams}}{{baseName}}=params.get('{{paramName}}'){{#hasMore}}, {{/hasMore}}{{/queryParams}}))
formParams = {} header_params = remove_none(dict({{#headerParams}}{{baseName}}=params.get('{{paramName}}'){{#hasMore}}, {{/hasMore}}{{/headerParams}}))
files = {} form_params = remove_none(dict({{#formParams}}{{^isFile}}{{baseName}}=params.get('{{paramName}}'){{#hasMore}}, {{/hasMore}}{{/isFile}}{{/formParams}}))
bodyParam = None files = remove_none(dict({{#formParams}}{{#isFile}}{{baseName}}=params.get('{{paramName}}'){{#hasMore}}, {{/hasMore}}{{/isFile}}{{/formParams}}))
body_params = {{#bodyParam}}params.get('{{paramName}}'){{/bodyParam}}{{^bodyParam}}None{{/bodyParam}}
accepts = [{{#produces}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/produces}}] accepts = [{{#produces}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/produces}}]
headerParams['Accept'] = ', '.join(accepts) header_params['Accept'] = ', '.join(accepts)
content_types = [{{#consumes}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/consumes}}] content_types = [{{#consumes}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/consumes}}]
headerParams['Content-Type'] = content_types[0] if len(content_types) > 0 else 'application/json' header_params['Content-Type'] = content_types[0] if len(content_types) > 0 else 'application/json'
{{#queryParams}}
if ('{{paramName}}' in params):
queryParams['{{baseName}}'] = self.apiClient.toPathValue(params['{{paramName}}'])
{{/queryParams}}
{{#headerParams}}
if ('{{paramName}}' in params):
headerParams['{{baseName}}'] = params['{{paramName}}']
{{/headerParams}}
{{#pathParams}}
if ('{{paramName}}' in params):
replacement = str(self.apiClient.toPathValue(params['{{paramName}}']))
replacement = urllib.quote(replacement)
resourcePath = resourcePath.replace('{' + '{{baseName}}' + '}',
replacement)
{{/pathParams}}
{{#formParams}}
if ('{{paramName}}' in params):
{{#notFile}}formParams['{{baseName}}'] = params['{{paramName}}']{{/notFile}}{{#isFile}}files['{{baseName}}'] = params['{{paramName}}']{{/isFile}}
{{/formParams}}
{{#bodyParam}}
if ('{{paramName}}' in params):
bodyParam = params['{{paramName}}']
{{/bodyParam}}
postData = (formParams if formParams else bodyParam)
response = self.apiClient.callAPI(resourcePath, method, queryParams,
postData, headerParams, files=files)
response = self.api_client.call_api(resource_path, method, path_params, query_params, header_params,
body=body_params, post_params=form_params, files=files,
response={{#returnType}}'{{returnType}}'{{/returnType}}{{^returnType}}None{{/returnType}})
{{#returnType}} {{#returnType}}
if not response: return response
return None {{/returnType}}{{/operation}}
responseObject = self.apiClient.deserialize(response, '{{returnType}}')
return responseObject
{{/returnType}}
{{newline}}
{{newline}}
{{/operation}}
{{newline}}
{{/operations}} {{/operations}}
{{newline}}

View File

@ -20,33 +20,40 @@ Copyright 2015 Reverb Technologies, Inc.
{{#model}} {{#model}}
class {{classname}}(object): class {{classname}}(object):
"""NOTE: This class is auto generated by the swagger code generator program. """
Do not edit the class manually.""" NOTE: This class is auto generated by the swagger code generator program.
Do not edit the class manually.
"""
def __init__(self): def __init__(self):
""" """
Attributes: Swagger model
swaggerTypes (dict): The key is attribute name and the value is attribute type.
attributeMap (dict): The key is attribute name and the value is json key in definition. :param dict swaggerTypes: The key is attribute name and the value is attribute type.
:param dict attributeMap: The key is attribute name and the value is json key in definition.
""" """
self.swaggerTypes = { self.swagger_types = {
{{#vars}} {{#vars}}'{{name}}': '{{{datatype}}}'{{#hasMore}},
'{{name}}': '{{{datatype}}}'{{#hasMore}}, {{/hasMore}}{{/vars}}
{{/hasMore}}
{{/vars}}{{newline}}
} }
self.attributeMap = { self.attribute_map = {
{{#vars}} {{#vars}}'{{name}}': '{{baseName}}'{{#hasMore}},
'{{name}}': '{{baseName}}'{{#hasMore}},{{/hasMore}} {{/hasMore}}{{/vars}}
{{/vars}}
} }
{{#vars}} {{#vars}}
{{#description}}#{{description}} {{#description}}# {{description}}{{/description}}
{{/description}} self.{{name}} = None # {{{datatype}}}
self.{{name}} = None # {{{datatype}}}
{{/vars}} {{/vars}}
def __repr__(self):
properties = []
for p in self.__dict__:
if p != 'swaggerTypes' and p != 'attributeMap':
properties.append('{prop}={val!r}'.format(prop=p, val=self.__dict__[p]))
return '<{name} {props}>'.format(name=__name__, props=' '.join(properties))
{{/model}} {{/model}}
{{/models}} {{/models}}

View File

@ -0,0 +1,223 @@
# coding: utf-8
import sys
import io
import json
# python 2 and python 3 compatibility library
from six import iteritems
try:
import urllib3
except ImportError:
raise ImportError('Swagger python client requires urllib3.')
try:
# for python3
from urllib.parse import urlencode
except ImportError:
# for python2
from urllib import urlencode
class RESTResponse(io.IOBase):
def __init__(self, resp):
self.urllib3_response = resp
self.status = resp.status
self.reason = resp.reason
self.data = resp.data
def getheaders(self):
"""
Returns a dictionary of the response headers.
"""
return self.urllib3_response.getheaders()
def getheader(self, name, default=None):
"""
Returns a given response header.
"""
return self.urllib3_response.getheader(name, default)
class RESTClientObject(object):
def __init__(self, pools_size=4):
self.pool_manager = urllib3.PoolManager(
num_pools=pools_size
)
def request(self, method, url, query_params=None, headers=None,
body=None, post_params=None):
"""
:param method: http request method
:param url: http request url
:param query_params: query parameters in the url
:param headers: http request headers
:param body: request json body, for `application/json`
:param post_params: request post parameters, `application/x-www-form-urlencode`
and `multipart/form-data`
:param raw_response: if return the raw response
"""
method = method.upper()
assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT', 'PATCH']
if post_params and body:
raise ValueError("body parameter cannot be used with post_params parameter.")
post_params = post_params or {}
headers = headers or {}
if 'Content-Type' not in headers:
headers['Content-Type'] = 'application/json'
# For `POST`, `PUT`, `PATCH`
if method in ['POST', 'PUT', 'PATCH']:
if query_params:
url += '?' + urlencode(query_params)
if headers['Content-Type'] == 'application/json':
r = self.pool_manager.request(method, url,
body=json.dumps(body),
headers=headers)
if headers['Content-Type'] == 'application/x-www-form-urlencoded':
r = self.pool_manager.request(method, url,
fields=post_params,
encode_multipart=False,
headers=headers)
if headers['Content-Type'] == 'multipart/form-data':
# must del headers['Content-Type'], or the correct Content-Type
# which generated by urllib3 will be overwritten.
del headers['Content-Type']
r = self.pool_manager.request(method, url,
fields=post_params,
encode_multipart=True,
headers=headers)
# For `GET`, `HEAD`, `DELETE`
else:
r = self.pool_manager.request(method, url,
fields=query_params,
headers=headers)
r = RESTResponse(r)
if r.status not in range(200, 206):
raise ErrorResponse(r)
return self.process_response(r)
def process_response(self, response):
# In the python 3, the response.data is bytes.
# we need to decode it to string.
if sys.version_info > (3,):
data = response.data.decode('utf8')
else:
data = response.data
try:
resp = json.loads(data)
except ValueError:
resp = data
return resp
def GET(self, url, headers=None, query_params=None):
return self.request("GET", url, headers=headers, query_params=query_params)
def HEAD(self, url, headers=None, query_params=None):
return self.request("HEAD", url, headers=headers, query_params=query_params)
def DELETE(self, url, headers=None, query_params=None):
return self.request("DELETE", url, headers=headers, query_params=query_params)
def POST(self, url, headers=None, post_params=None, body=None):
return self.request("POST", url, headers=headers, post_params=post_params, body=body)
def PUT(self, url, headers=None, post_params=None, body=None):
return self.request("PUT", url, headers=headers, post_params=post_params, body=body)
def PATCH(self, url, headers=None, post_params=None, body=None):
return self.request("PATCH", url, headers=headers, post_params=post_params, body=body)
class ErrorResponse(Exception):
"""
Non-2xx HTTP response
"""
def __init__(self, http_resp):
self.status = http_resp.status
self.reason = http_resp.reason
self.body = http_resp.data
self.headers = http_resp.getheaders()
# In the python 3, the self.body is bytes.
# we need to decode it to string.
if sys.version_info > (3,):
data = self.body.decode('utf8')
else:
data = self.body
try:
self.body = json.loads(data)
except ValueError:
self.body = data
def __str__(self):
"""
Custom error response messages
"""
return "({0})\nReason: {1}\nHeader: {2}\nBody: {3}\n".\
format(self.status, self.reason, self.headers, self.body)
class RESTClient(object):
"""
A class with all class methods to perform JSON requests.
"""
IMPL = RESTClientObject()
@classmethod
def request(cls, *n, **kw):
"""
Perform a REST request and parse the response.
"""
return cls.IMPL.request(*n, **kw)
@classmethod
def GET(cls, *n, **kw):
"""
Perform a GET request using `RESTClient.request()`.
"""
return cls.IMPL.GET(*n, **kw)
@classmethod
def HEAD(cls, *n, **kw):
"""
Perform a HEAD request using `RESTClient.request()`.
"""
return cls.IMPL.GET(*n, **kw)
@classmethod
def POST(cls, *n, **kw):
"""
Perform a POST request using `RESTClient.request()`
"""
return cls.IMPL.POST(*n, **kw)
@classmethod
def PUT(cls, *n, **kw):
"""
Perform a PUT request using `RESTClient.request()`
"""
return cls.IMPL.PUT(*n, **kw)
@classmethod
def PATCH(cls, *n, **kw):
"""
Perform a PATCH request using `RESTClient.request()`
"""
return cls.IMPL.PATCH(*n, **kw)
@classmethod
def DELETE(cls, *n, **kw):
"""
Perform a DELETE request using `RESTClient.request()`
"""
return cls.IMPL.DELETE(*n, **kw)

View File

@ -12,7 +12,7 @@ from setuptools import setup, find_packages
# Try reading the setuptools documentation: # Try reading the setuptools documentation:
# http://pypi.python.org/pypi/setuptools # http://pypi.python.org/pypi/setuptools
REQUIRES = [] REQUIRES = ["urllib3 >= 1.10", "six >= 1.9"]
setup( setup(
name="{{module}}", name="{{module}}",
@ -30,3 +30,11 @@ setup(
) )
{{/hasMore}}{{/apis}}{{/apiInfo}} {{/hasMore}}{{/apis}}{{/apiInfo}}

View File

@ -6,118 +6,109 @@ server communication, and is invariant across implementations. Specifics of
the methods and models for each application are generated from the Swagger the methods and models for each application are generated from the Swagger
templates.""" templates."""
import sys from __future__ import absolute_import
from . import models
from .rest import RESTClient
import os import os
import re import re
import urllib import urllib
import urllib2
import httplib
import json import json
import datetime import datetime
import mimetypes import mimetypes
import random import random
import string
import models # python 2 and python 3 compatibility library
from six import iteritems
try:
# for python3
from urllib.parse import quote
except ImportError:
# for python2
from urllib import quote
class ApiClient(object): class ApiClient(object):
"""Generic API client for Swagger client library builds
Attributes:
host: The base path for the server to call
headerName: a header to pass when making calls to the API
headerValue: a header value to pass when making calls to the API
""" """
def __init__(self, host=None, headerName=None, headerValue=None): Generic API client for Swagger client library builds
self.defaultHeaders = {}
if (headerName is not None): :param host: The base path for the server to call
self.defaultHeaders[headerName] = headerValue :param header_name: a header to pass when making calls to the API
:param header_value: a header value to pass when making calls to the API
"""
def __init__(self, host=None, header_name=None, header_value=None):
self.default_headers = {}
if header_name is not None:
self.default_headers[header_name] = header_value
self.host = host self.host = host
self.cookie = None self.cookie = None
self.boundary = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(30))
# Set default User-Agent. # Set default User-Agent.
self.user_agent = 'Python-Swagger' self.user_agent = 'Python-Swagger'
@property @property
def user_agent(self): def user_agent(self):
return self.defaultHeaders['User-Agent'] return self.default_headers['User-Agent']
@user_agent.setter @user_agent.setter
def user_agent(self, value): def user_agent(self, value):
self.defaultHeaders['User-Agent'] = value self.default_headers['User-Agent'] = value
def setDefaultHeader(self, headerName, headerValue): def set_default_header(self, header_name, header_value):
self.defaultHeaders[headerName] = headerValue self.default_headers[header_name] = header_value
def callAPI(self, resourcePath, method, queryParams, postData, def call_api(self, resource_path, method, path_params=None, query_params=None, header_params=None,
headerParams=None, files=None): body=None, post_params=None, files=None, response=None):
url = self.host + resourcePath
mergedHeaderParams = self.defaultHeaders.copy()
mergedHeaderParams.update(headerParams)
headers = {}
if mergedHeaderParams:
for param, value in mergedHeaderParams.iteritems():
headers[param] = ApiClient.sanitizeForSerialization(value)
# headers parameters
headers = self.default_headers.copy()
headers.update(header_params)
if self.cookie: if self.cookie:
headers['Cookie'] = ApiClient.sanitizeForSerialization(self.cookie) headers['Cookie'] = self.cookie
if headers:
headers = ApiClient.sanitize_for_serialization(headers)
data = None # path parameters
if path_params:
path_params = ApiClient.sanitize_for_serialization(path_params)
for k, v in iteritems(path_params):
replacement = quote(str(self.to_path_value(v)))
resource_path = resource_path.replace('{' + k + '}', replacement)
if queryParams: # query parameters
# Need to remove None values, these should not be sent if query_params:
sentQueryParams = {} query_params = ApiClient.sanitize_for_serialization(query_params)
for param, value in queryParams.items(): query_params = {k: self.to_path_value(v) for k, v in iteritems(query_params)}
if value is not None:
sentQueryParams[param] = ApiClient.sanitizeForSerialization(value)
url = url + '?' + urllib.urlencode(sentQueryParams)
if method in ['GET']: # post parameters
#Options to add statements later on and for compatibility if post_params:
pass post_params = self.prepare_post_parameters(post_params, files)
post_params = ApiClient.sanitize_for_serialization(post_params)
elif method in ['POST', 'PUT', 'DELETE']: # body
if postData: if body:
postData = ApiClient.sanitizeForSerialization(postData) body = ApiClient.sanitize_for_serialization(body)
if 'Content-Type' not in headers:
headers['Content-Type'] = 'application/json'
data = json.dumps(postData)
elif headers['Content-Type'] == 'application/json':
data = json.dumps(postData)
elif headers['Content-Type'] == 'multipart/form-data':
data = self.buildMultipartFormData(postData, files)
headers['Content-Type'] = 'multipart/form-data; boundary={0}'.format(self.boundary)
headers['Content-length'] = str(len(data))
else:
data = urllib.urlencode(postData)
# request url
url = self.host + resource_path
# perform request and return response
response_data = self.request(method, url, query_params=query_params, headers=headers,
post_params=post_params, body=body)
# deserialize response data
if response:
return self.deserialize(response_data, response)
else: else:
raise Exception('Method ' + method + ' is not recognized.') return None
request = MethodRequest(method=method, url=url, headers=headers, def to_path_value(self, obj):
data=data) """
Convert a string or object to a path-friendly value
:param obj: object or string value
# Make the request :return string: quoted value
response = urllib2.urlopen(request)
if 'Set-Cookie' in response.headers:
self.cookie = response.headers['Set-Cookie']
string = response.read()
try:
data = json.loads(string)
except ValueError: # PUT requests don't return anything
data = None
return data
def toPathValue(self, obj):
"""Convert a string or object to a path-friendly value
Args:
obj -- object or string value
Returns:
string -- quoted value
""" """
if type(obj) == list: if type(obj) == list:
return ','.join(obj) return ','.join(obj)
@ -125,12 +116,12 @@ class ApiClient(object):
return str(obj) return str(obj)
@staticmethod @staticmethod
def sanitizeForSerialization(obj): def sanitize_for_serialization(obj):
""" """
Sanitize an object for Request. Sanitize an object for Request.
If obj is None, return None. If obj is None, return None.
If obj is str, int, long, float, bool, return directly. If obj is str, int, float, bool, return directly.
If obj is datetime.datetime, datetime.date convert to string in iso8601 format. If obj is datetime.datetime, datetime.date convert to string in iso8601 format.
If obj is list, santize each element in the list. If obj is list, santize each element in the list.
If obj is dict, return the dict. If obj is dict, return the dict.
@ -138,113 +129,80 @@ class ApiClient(object):
""" """
if isinstance(obj, type(None)): if isinstance(obj, type(None)):
return None return None
elif isinstance(obj, (str, int, long, float, bool, file)): elif isinstance(obj, (str, int, float, bool, tuple)):
return obj return obj
elif isinstance(obj, list): elif isinstance(obj, list):
return [ApiClient.sanitizeForSerialization(subObj) for subObj in obj] return [ApiClient.sanitize_for_serialization(sub_obj) for sub_obj in obj]
elif isinstance(obj, (datetime.datetime, datetime.date)): elif isinstance(obj, (datetime.datetime, datetime.date)):
return obj.isoformat() return obj.isoformat()
else: else:
if isinstance(obj, dict): if isinstance(obj, dict):
objDict = obj obj_dict = obj
else: else:
# Convert model obj to dict except attributes `swaggerTypes`, `attributeMap` # Convert model obj to dict except attributes `swagger_types`, `attribute_map`
# and attributes which value is not None. # and attributes which value is not None.
# Convert attribute name to json key in model definition for request. # Convert attribute name to json key in model definition for request.
objDict = {obj.attributeMap[key]: val obj_dict = {obj.attribute_map[key]: val
for key, val in obj.__dict__.iteritems() for key, val in iteritems(obj.__dict__)
if key != 'swaggerTypes' and key != 'attributeMap' and val is not None} if key != 'swagger_types' and key != 'attribute_map' and val is not None}
return {key: ApiClient.sanitizeForSerialization(val) return {key: ApiClient.sanitize_for_serialization(val)
for (key, val) in objDict.iteritems()} for key, val in iteritems(obj_dict)}
def buildMultipartFormData(self, postData, files): def deserialize(self, obj, obj_class):
def escape_quotes(s): """
return s.replace('"', '\\"') Derialize a JSON string into an object.
lines = [] :param obj: string or object to be deserialized
:param obj_class: class literal for deserialzied object, or string of class name
for name, value in postData.items(): :return object: deserialized object
lines.extend(( """
'--{0}'.format(self.boundary), # Have to accept obj_class as string or actual type. Type could be a
'Content-Disposition: form-data; name="{0}"'.format(escape_quotes(name)),
'',
str(value),
))
for name, filepath in files.items():
f = open(filepath, 'r')
filename = filepath.split('/')[-1]
mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
lines.extend((
'--{0}'.format(self.boundary),
'Content-Disposition: form-data; name="{0}"; filename="{1}"'.format(escape_quotes(name), escape_quotes(filename)),
'Content-Type: {0}'.format(mimetype),
'',
f.read()
))
lines.extend((
'--{0}--'.format(self.boundary),
''
))
return '\r\n'.join(lines)
def deserialize(self, obj, objClass):
"""Derialize a JSON string into an object.
Args:
obj -- string or object to be deserialized
objClass -- class literal for deserialzied object, or string
of class name
Returns:
object -- deserialized object"""
# Have to accept objClass as string or actual type. Type could be a
# native Python type, or one of the model classes. # native Python type, or one of the model classes.
if type(objClass) == str: if type(obj_class) == str:
if 'list[' in objClass: if 'list[' in obj_class:
match = re.match('list\[(.*)\]', objClass) match = re.match('list\[(.*)\]', obj_class)
subClass = match.group(1) sub_class = match.group(1)
return [self.deserialize(subObj, subClass) for subObj in obj] return [self.deserialize(sub_obj, sub_class) for sub_obj in obj]
if (objClass in ['int', 'float', 'long', 'dict', 'list', 'str', 'bool', 'datetime']): if obj_class in ['int', 'float', 'dict', 'list', 'str', 'bool', 'datetime']:
objClass = eval(objClass) obj_class = eval(obj_class)
else: # not a native type, must be model class else: # not a native type, must be model class
objClass = eval('models.' + objClass) obj_class = eval('models.' + obj_class)
if objClass in [int, long, float, dict, list, str, bool]: if obj_class in [int, float, dict, list, str, bool]:
return objClass(obj) return obj_class(obj)
elif objClass == datetime: elif obj_class == datetime:
return self.__parse_string_to_datetime(obj) return self.__parse_string_to_datetime(obj)
instance = objClass() instance = obj_class()
for attr, attrType in instance.swaggerTypes.iteritems(): for attr, attr_type in iteritems(instance.swagger_types):
if obj is not None and instance.attributeMap[attr] in obj and type(obj) in [list, dict]: if obj is not None and instance.attribute_map[attr] in obj and type(obj) in [list, dict]:
value = obj[instance.attributeMap[attr]] value = obj[instance.attribute_map[attr]]
if attrType in ['str', 'int', 'long', 'float', 'bool']: if attr_type in ['str', 'int', 'float', 'bool']:
attrType = eval(attrType) attr_type = eval(attr_type)
try: try:
value = attrType(value) value = attr_type(value)
except UnicodeEncodeError: except UnicodeEncodeError:
value = unicode(value) value = unicode(value)
except TypeError: except TypeError:
value = value value = value
setattr(instance, attr, value) setattr(instance, attr, value)
elif (attrType == 'datetime'): elif attr_type == 'datetime':
setattr(instance, attr, self.__parse_string_to_datetime(value)) setattr(instance, attr, self.__parse_string_to_datetime(value))
elif 'list[' in attrType: elif 'list[' in attr_type:
match = re.match('list\[(.*)\]', attrType) match = re.match('list\[(.*)\]', attr_type)
subClass = match.group(1) sub_class = match.group(1)
subValues = [] sub_values = []
if not value: if not value:
setattr(instance, attr, None) setattr(instance, attr, None)
else: else:
for subValue in value: for sub_value in value:
subValues.append(self.deserialize(subValue, subClass)) sub_values.append(self.deserialize(sub_value, sub_class))
setattr(instance, attr, subValues) setattr(instance, attr, sub_values)
else: else:
setattr(instance, attr, self.deserialize(value, attrType)) setattr(instance, attr, self.deserialize(value, attr_type))
return instance return instance
@ -260,16 +218,42 @@ class ApiClient(object):
except ImportError: except ImportError:
return string return string
class MethodRequest(urllib2.Request): def request(self, method, url, query_params=None, headers=None, post_params=None, body=None):
def __init__(self, *args, **kwargs): """
"""Construct a MethodRequest. Usage is the same as for Perform http request using RESTClient.
`urllib2.Request` except it also takes an optional `method` """
keyword argument. If supplied, `method` will be used instead of if method == "GET":
the default.""" return RESTClient.GET(url, query_params=query_params, headers=headers)
elif method == "HEAD":
return RESTClient.HEAD(url, query_params=query_params, headers=headers)
elif method == "POST":
return RESTClient.POST(url, headers=headers, post_params=post_params, body=body)
elif method == "PUT":
return RESTClient.PUT(url, headers=headers, post_params=post_params, body=body)
elif method == "PATCH":
return RESTClient.PATCH(url, headers=headers, post_params=post_params, body=body)
elif method == "DELETE":
return RESTClient.DELETE(url, query_params=query_params, headers=headers)
else:
raise ValueError("http method must be `GET`, `HEAD`, `POST`, `PATCH`, `PUT` or `DELETE`")
def prepare_post_parameters(self, post_params=None, files=None):
params = {}
if post_params:
params.update(post_params)
if files:
for k, v in iteritems(files):
with open(v, 'rb') as f:
filename = os.path.basename(f.name)
filedata = f.read()
mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
params[k] = tuple([filename, filedata, mimetype])
return params
if 'method' in kwargs:
self.method = kwargs.pop('method')
return urllib2.Request.__init__(self, *args, **kwargs)
def get_method(self):
return getattr(self, 'method', urllib2.Request.get_method(self))

View File

@ -0,0 +1,17 @@
from six import iteritems
def remove_none(obj):
if isinstance(obj, (list, tuple, set)):
return type(obj)(remove_none(x) for x in obj if x is not None)
elif isinstance(obj, dict):
return type(obj)((remove_none(k), remove_none(v))
for k, v in iteritems(obj) if k is not None and v is not None)
else:
return obj
def inspect_vars(obj):
if not hasattr(obj, '__dict__'):
return obj
else:
return {k: inspect_vars(getattr(obj, k)) for k in dir(obj)}

View File

@ -1,57 +1,59 @@
require "uri" require "uri"
module {{moduleName}}
{{#operations}} {{#operations}}
class {{classname}} class {{classname}}
basePath = "{{basePath}}" basePath = "{{basePath}}"
# apiInvoker = APIInvoker # apiInvoker = APIInvoker
{{#operation}} {{#operation}}
{{newline}} {{newline}}
# {{summary}} # {{summary}}
# {{notes}} # {{notes}}
{{#allParams}}{{#required}} # @param {{paramName}} {{description}} {{#allParams}}{{#required}} # @param {{paramName}} {{description}}
{{/required}}{{/allParams}} # @param [Hash] opts the optional parameters {{/required}}{{/allParams}} # @param [Hash] opts the optional parameters
{{#allParams}}{{^required}} # @option opts [{{dataType}}] :{{paramName}} {{description}} {{#allParams}}{{^required}} # @option opts [{{dataType}}] :{{paramName}} {{description}}
{{/required}}{{/allParams}} # @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{/required}}{{/allParams}} # @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}
def self.{{nickname}}({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}opts = {}) def self.{{nickname}}({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}opts = {})
{{#allParams}}{{#required}} {{#allParams}}{{#required}}
# verify the required parameter '{{paramName}}' is set # verify the required parameter '{{paramName}}' is set
raise "Missing the required parameter '{{paramName}}' when calling {{nickname}}" if {{{paramName}}}.nil? raise "Missing the required parameter '{{paramName}}' when calling {{nickname}}" if {{{paramName}}}.nil?
{{/required}}{{/allParams}} {{/required}}{{/allParams}}
# resource path # resource path
path = "{{path}}".sub('{format}','json'){{#pathParams}}.sub('{' + '{{baseName}}' + '}', {{paramName}}.to_s){{/pathParams}} path = "{{path}}".sub('{format}','json'){{#pathParams}}.sub('{' + '{{baseName}}' + '}', {{paramName}}.to_s){{/pathParams}}
# query parameters # query parameters
query_params = {}{{#queryParams}}{{#required}} query_params = {}{{#queryParams}}{{#required}}
query_params[:'{{{baseName}}}'] = {{{paramName}}}{{/required}}{{/queryParams}}{{#queryParams}}{{^required}} query_params[:'{{{baseName}}}'] = {{{paramName}}}{{/required}}{{/queryParams}}{{#queryParams}}{{^required}}
query_params[:'{{{baseName}}}'] = opts[:'{{{paramName}}}'] if opts[:'{{{paramName}}}']{{/required}}{{/queryParams}} query_params[:'{{{baseName}}}'] = opts[:'{{{paramName}}}'] if opts[:'{{{paramName}}}']{{/required}}{{/queryParams}}
# header parameters # header parameters
header_params = {} header_params = {}
# HTTP header 'Accept' (if needed) # HTTP header 'Accept' (if needed)
_header_accept = [{{#produces}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/produces}}] _header_accept = [{{#produces}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/produces}}]
_header_accept_result = Swagger::Request.select_header_accept(_header_accept) and header_params['Accept'] = _header_accept_result _header_accept_result = Swagger::Request.select_header_accept(_header_accept) and header_params['Accept'] = _header_accept_result
# HTTP header 'Content-Type' # HTTP header 'Content-Type'
_header_content_type = [{{#consumes}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/consumes}}] _header_content_type = [{{#consumes}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/consumes}}]
header_params['Content-Type'] = Swagger::Request.select_header_content_type(_header_content_type){{#headerParams}}{{#required}} header_params['Content-Type'] = Swagger::Request.select_header_content_type(_header_content_type){{#headerParams}}{{#required}}
header_params[:'{{{baseName}}}'] = {{{paramName}}}{{/required}}{{/headerParams}}{{#headerParams}}{{^required}} header_params[:'{{{baseName}}}'] = {{{paramName}}}{{/required}}{{/headerParams}}{{#headerParams}}{{^required}}
header_params[:'{{{baseName}}}'] = opts[:'{{{paramName}}}'] if opts[:'{{{paramName}}}']{{/required}}{{/headerParams}} header_params[:'{{{baseName}}}'] = opts[:'{{{paramName}}}'] if opts[:'{{{paramName}}}']{{/required}}{{/headerParams}}
# form parameters # form parameters
form_params = {}{{#formParams}}{{#required}} form_params = {}{{#formParams}}{{#required}}
form_params["{{baseName}}"] = {{paramName}}{{/required}}{{/formParams}}{{#formParams}}{{^required}} form_params["{{baseName}}"] = {{paramName}}{{/required}}{{/formParams}}{{#formParams}}{{^required}}
form_params["{{baseName}}"] = opts[:'{{paramName}}'] if opts[:'{{paramName}}']{{/required}}{{/formParams}} form_params["{{baseName}}"] = opts[:'{{paramName}}'] if opts[:'{{paramName}}']{{/required}}{{/formParams}}
# http body (model) # http body (model)
{{^bodyParam}}post_body = nil {{^bodyParam}}post_body = nil
{{/bodyParam}}{{#bodyParam}}post_body = Swagger::Request.object_to_http_body({{#required}}{{{paramName}}}{{/required}}{{^required}}opts[:'{{{paramName}}}']{{/required}}) {{/bodyParam}}{{#bodyParam}}post_body = Swagger::Request.object_to_http_body({{#required}}{{{paramName}}}{{/required}}{{^required}}opts[:'{{{paramName}}}']{{/required}})
{{/bodyParam}} {{/bodyParam}}
{{#returnType}}response = Swagger::Request.new(:{{httpMethod}}, path, {:params => query_params, :headers => header_params, :form_params => form_params, :body => post_body}).make.body {{#returnType}}response = Swagger::Request.new(:{{httpMethod}}, path, {:params => query_params, :headers => header_params, :form_params => form_params, :body => post_body}).make.body
{{#returnContainer}}response.map {|response| {{/returnContainer}}obj = {{returnBaseType}}.new() and obj.build_from_hash(response){{#returnContainer}} }{{/returnContainer}}{{/returnType}}{{^returnType}} Swagger::Request.new(:{{httpMethod}}, path, {:params => query_params,:headers => header_params, :form_params => form_params, :body => post_body}).make{{/returnType}} {{#returnContainer}}response.map {|response| {{/returnContainer}}obj = {{returnBaseType}}.new() and obj.build_from_hash(response){{#returnContainer}} }{{/returnContainer}}{{/returnType}}{{^returnType}} Swagger::Request.new(:{{httpMethod}}, path, {:params => query_params,:headers => header_params, :form_params => form_params, :body => post_body}).make{{/returnType}}
end end
{{/operation}} {{/operation}}
end end
{{/operations}} {{/operations}}
end

View File

@ -1,83 +1,83 @@
# base class containing fundamental method such as to_hash, build_from_hash and more module {{moduleName}}
class BaseObject # base class containing fundamental method such as to_hash, build_from_hash and more
class BaseObject
# return the object in the form of hash # return the object in the form of hash
def to_body def to_body
body = {} body = {}
self.class.attribute_map.each_pair do |key, value| self.class.attribute_map.each_pair do |key, value|
body[value] = self.send(key) unless self.send(key).nil? body[value] = self.send(key) unless self.send(key).nil?
end
body
end end
body
end
# build the object from hash # build the object from hash
def build_from_hash(attributes) def build_from_hash(attributes)
return nil unless attributes.is_a?(Hash) return nil unless attributes.is_a?(Hash)
self.class.swagger_types.each_pair do |key, type| self.class.swagger_types.each_pair do |key, type|
if type =~ /^array\[(.*)\]/i if type =~ /^array\[(.*)\]/i
if attributes[self.class.attribute_map[key]].is_a?(Array) if attributes[self.class.attribute_map[key]].is_a?(Array)
self.send("#{key}=", attributes[self.class.attribute_map[key]].map{ |v| _deserialize($1, v) } ) self.send("#{key}=", attributes[self.class.attribute_map[key]].map{ |v| _deserialize($1, v) } )
else
#TODO show warning in debug mode
end
elsif !attributes[self.class.attribute_map[key]].nil?
self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]]))
else else
#TODO show warning in debug mode # data not found in attributes(hash), not an issue as the data can be optional
end
elsif !attributes[self.class.attribute_map[key]].nil?
self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]]))
else
# data not found in attributes(hash), not an issue as the data can be optional
end
end
self
end
def _deserialize(type, value)
case type.to_sym
when :DateTime
DateTime.parse(value)
when :string
value.to_s
when :int
value.to_i
when :double
value.to_f
when :boolean
if value =~ /^(true|t|yes|y|1)$/i
true
else
false
end
else # model
_model = Object.const_get(type).new
_model.build_from_hash(value)
end
end
# to_body is an alias to to_body (backward compatibility)
def to_hash
hash = {}
self.class.attribute_map.each_pair do |key, value|
if self.send(key).is_a?(Array)
next if self.send(key).empty?
hash[value] = self.send(key).select{|v| !v.nil?}.map{ |v| _to_hash v} unless self.send(key).nil?
else
unless (_tmp_value = _to_hash self.send(key)).nil?
hash[value] = _tmp_value
end end
end end
end
hash
end
# Method to output non-array value in the form of hash self
# For object, use to_hash. Otherwise, just return the value
def _to_hash(value)
if value.respond_to? :to_hash
value.to_hash
else
value
end end
end
def _deserialize(type, value)
case type.to_sym
when :DateTime
DateTime.parse(value)
when :string
value.to_s
when :int
value.to_i
when :double
value.to_f
when :boolean
if value =~ /^(true|t|yes|y|1)$/i
true
else
false
end
else # model
_model = {{moduleName}}.const_get(type).new
_model.build_from_hash(value)
end
end
# to_body is an alias to to_body (backward compatibility)
def to_hash
hash = {}
self.class.attribute_map.each_pair do |key, value|
if self.send(key).is_a?(Array)
next if self.send(key).empty?
hash[value] = self.send(key).select{|v| !v.nil?}.map{ |v| _to_hash v} unless self.send(key).nil?
else
unless (_tmp_value = _to_hash self.send(key)).nil?
hash[value] = _tmp_value
end
end
end
hash
end
# Method to output non-array value in the form of hash
# For object, use to_hash. Otherwise, just return the value
def _to_hash(value)
if value.respond_to? :to_hash
value.to_hash
else
value
end
end
end
end end

View File

@ -1,41 +1,40 @@
require_relative 'base_object' module {{moduleName}}
{{#models}} # {{description}}
{{#models}}#{{description}} {{#model}} class {{classname}} < BaseObject
{{#model}}class {{classname}} < BaseObject attr_accessor {{#vars}}:{{{name}}}{{#hasMore}}, {{/hasMore}}{{/vars}}{{newline}}
attr_accessor {{#vars}}:{{{name}}}{{#hasMore}}, {{/hasMore}}{{/vars}}{{newline}} # attribute mapping from ruby-style variable name to JSON key
# attribute mapping from ruby-style variable name to JSON key def self.attribute_map
def self.attribute_map {
{ {{#vars}}
{{#vars}} # {{description}}
# {{description}} :'{{{name}}}' => :'{{{baseName}}}'{{#hasMore}},{{/hasMore}}
:'{{{name}}}' => :'{{{baseName}}}'{{#hasMore}},{{/hasMore}} {{/vars}}
{{/vars}} }
}
end
# attribute type
def self.swagger_types
{
{{#vars}}:'{{{name}}}' => :'{{{datatype}}}'{{#hasMore}},{{/hasMore}}
{{/vars}}
}
end
def initialize(attributes = {})
return if !attributes.is_a?(Hash) || attributes.empty?
# convert string to symbol for hash key
attributes = attributes.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
{{#vars}}
if attributes[:'{{{baseName}}}']
{{#isContainer}}if (value = attributes[:'{{{baseName}}}']).is_a?(Array)
@{{{name}}} = value
end{{/isContainer}}{{^isContainer}}@{{{name}}} = attributes[:'{{{baseName}}}']{{/isContainer}}
end end
{{/vars}}
end
end # attribute type
def self.swagger_types
{
{{#vars}}:'{{{name}}}' => :'{{{datatype}}}'{{#hasMore}},{{/hasMore}}
{{/vars}}
}
end
def initialize(attributes = {})
return if !attributes.is_a?(Hash) || attributes.empty?
# convert string to symbol for hash key
attributes = attributes.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
{{#vars}}
if attributes[:'{{{baseName}}}']
{{#isContainer}}if (value = attributes[:'{{{baseName}}}']).is_a?(Array)
@{{{name}}} = value
end{{/isContainer}}{{^isContainer}}@{{{name}}} = attributes[:'{{{baseName}}}']{{/isContainer}}
end
{{/vars}}
end
end
{{/model}} {{/model}}
{{/models}} {{/models}}
end

View File

@ -1,33 +0,0 @@
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "swagger/version"
Gem::Specification.new do |s|
s.name = "{{artifactId}}"
s.version = Swagger::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ["Zeke Sikelianos", "Tony Tam"]
s.email = ["zeke@wordnik.com", "tony@wordnik.com"]
s.homepage = "http://developer.wordnik.com"
s.summary = %q{A ruby wrapper for the swagger APIs}
s.description = %q{This gem maps to a swagger API}
s.rubyforge_project = "{{artifactId}}"
s.add_dependency 'typhoeus', '>=0.2.1'
s.add_dependency 'addressable', '>=2.2.4'
s.add_dependency 'json', '>=1.4.6'
s.add_development_dependency 'rspec', '>=2.5.0'
s.add_development_dependency 'vcr', '>=1.5.1'
s.add_development_dependency 'webmock', '>=1.6.2'
s.add_development_dependency 'autotest'
s.add_development_dependency 'autotest-rails-pure'
s.add_development_dependency 'autotest-growl'
s.add_development_dependency 'autotest-fsevent'
s.files = `find *`.split("\n").uniq.sort.select{|f| !f.empty? }
s.test_files = `find spec/*`.split("\n")
s.executables = []
s.require_paths = ["lib"]
end

View File

@ -1,5 +0,0 @@
require 'monkey'
require 'swagger'
Dir[File.join(File.dirname(__FILE__), "../lib/*.rb")].each {|file| require file if file !~ /swagger-client\.rb\z/ }
Dir[File.join(File.dirname(__FILE__), "../models/*.rb")].each {|file| require file }

View File

@ -1,86 +1,78 @@
require 'monkey'
require 'swagger/configuration'
require 'swagger/request'
require 'swagger/response'
require 'swagger/version'
require 'logger' require 'logger'
require 'json' require 'json'
module Swagger module {{moduleName}}
module Swagger
@configuration = Configuration.new class << self
attr_accessor :logger
class << self # A Swagger configuration object. Must act like a hash and return sensible
attr_accessor :logger # values for all Swagger configuration options. See Swagger::Configuration.
attr_accessor :configuration
# A Swagger configuration object. Must act like a hash and return sensible
# values for all Swagger configuration options. See Swagger::Configuration.
attr_accessor :configuration
attr_accessor :resources attr_accessor :resources
# Call this method to modify defaults in your initializers.
#
# @example
# Swagger.configure do |config|
# config.api_key = '1234567890abcdef' # required
# config.username = 'wordlover' # optional, but needed for user-related functions
# config.password = 'i<3words' # optional, but needed for user-related functions
# config.format = 'json' # optional, defaults to 'json'
# end
#
def configure
yield(configuration) if block_given?
# Configure logger. Default to use Rails # Call this method to modify defaults in your initializers.
self.logger ||= configuration.logger || (defined?(Rails) ? Rails.logger : Logger.new(STDOUT)) #
# @example
# Swagger.configure do |config|
# config.api_key = '1234567890abcdef' # required
# config.username = 'wordlover' # optional, but needed for user-related functions
# config.password = 'i<3words' # optional, but needed for user-related functions
# config.format = 'json' # optional, defaults to 'json'
# end
#
def configure
yield(configuration) if block_given?
# remove :// from scheme # Configure logger. Default to use Rails
configuration.scheme.sub!(/:\/\//, '') self.logger ||= configuration.logger || (defined?(Rails) ? Rails.logger : Logger.new(STDOUT))
# remove http(s):// and anything after a slash # remove :// from scheme
configuration.host.sub!(/https?:\/\//, '') configuration.scheme.sub!(/:\/\//, '')
configuration.host = configuration.host.split('/').first
# Add leading and trailing slashes to base_path # remove http(s):// and anything after a slash
configuration.base_path = "/#{configuration.base_path}".gsub(/\/+/, '/') configuration.host.sub!(/https?:\/\//, '')
configuration.base_path = "" if configuration.base_path == "/" configuration.host = configuration.host.split('/').first
end
# Add leading and trailing slashes to base_path
def authenticated? configuration.base_path = "/#{configuration.base_path}".gsub(/\/+/, '/')
Swagger.configuration.auth_token.present? configuration.base_path = "" if configuration.base_path == "/"
end end
def de_authenticate def authenticated?
Swagger.configuration.auth_token = nil Swagger.configuration.auth_token.present?
end end
def authenticate def de_authenticate
return if Swagger.authenticated? Swagger.configuration.auth_token = nil
end
if Swagger.configuration.username.blank? || Swagger.configuration.password.blank?
raise ClientError, "Username and password are required to authenticate." def authenticate
return if Swagger.authenticated?
if Swagger.configuration.username.blank? || Swagger.configuration.password.blank?
raise ClientError, "Username and password are required to authenticate."
end
request = Swagger::Request.new(
:get,
"account/authenticate/{username}",
:params => {
:username => Swagger.configuration.username,
:password => Swagger.configuration.password
}
)
response_body = request.response.body
Swagger.configuration.auth_token = response_body['token']
end end
request = Swagger::Request.new(
:get,
"account/authenticate/{username}",
:params => {
:username => Swagger.configuration.username,
:password => Swagger.configuration.password
}
)
response_body = request.response.body
Swagger.configuration.auth_token = response_body['token']
end end
end end
end
class ServerError < StandardError class ServerError < StandardError
end end
class ClientError < StandardError class ClientError < StandardError
end
end end

View File

@ -1,22 +1,19 @@
module Swagger module {{moduleName}}
module Swagger
class Configuration class Configuration
require 'swagger/version' attr_accessor :format, :api_key, :username, :password, :auth_token, :scheme, :host, :base_path, :user_agent, :logger, :inject_format, :force_ending_format, :camelize_params, :user_agent
attr_accessor :format, :api_key, :username, :password, :auth_token, :scheme, :host, :base_path, :user_agent, :logger, :inject_format, :force_ending_format, :camelize_params, :user_agent # Defaults go in here..
def initialize
# Defaults go in here.. @format = 'json'
def initialize @scheme = '{{scheme}}'
@format = 'json' @host = '{{host}}'
@scheme = '{{scheme}}' @base_path = '{{contextPath}}'
@host = '{{host}}' @user_agent = "ruby-swagger-#{Swagger::VERSION}"
@base_path = '{{contextPath}}' @inject_format = false
@user_agent = "ruby-swagger-#{Swagger::VERSION}" @force_ending_format = false
@inject_format = false @camelize_params = true
@force_ending_format = false end
@camelize_params = true
end end
end end
end end

View File

@ -1,263 +1,257 @@
module Swagger module {{moduleName}}
module Swagger
class Request
require 'uri'
require 'addressable/uri'
require 'typhoeus'
class Request attr_accessor :host, :path, :format, :params, :body, :http_method, :headers, :form_params
require 'uri'
require 'addressable/uri'
require 'typhoeus'
require "swagger/version"
attr_accessor :host, :path, :format, :params, :body, :http_method, :headers, :form_params # All requests must have an HTTP method and a path
# Optionals parameters are :params, :headers, :body, :format, :host
def initialize(http_method, path, attributes={})
attributes[:format] ||= Swagger.configuration.format
attributes[:params] ||= {}
# Set default headers
default_headers = {
'Content-Type' => "application/#{attributes[:format].downcase}",
:api_key => Swagger.configuration.api_key,
'User-Agent' => Swagger.configuration.user_agent
}
# All requests must have an HTTP method and a path # api_key from headers hash trumps the default, even if its value is blank
# Optionals parameters are :params, :headers, :body, :format, :host if attributes[:headers].present? && attributes[:headers].has_key?(:api_key)
# default_headers.delete(:api_key)
def initialize(http_method, path, attributes={}) end
attributes[:format] ||= Swagger.configuration.format
attributes[:params] ||= {}
# Set default headers # api_key from params hash trumps all others (headers and default_headers)
default_headers = { if attributes[:params].present? && attributes[:params].has_key?(:api_key)
'Content-Type' => "application/#{attributes[:format].downcase}", default_headers.delete(:api_key)
:api_key => Swagger.configuration.api_key, attributes[:headers].delete(:api_key) if attributes[:headers].present?
'User-Agent' => Swagger.configuration.user_agent end
}
# api_key from headers hash trumps the default, even if its value is blank # Merge argument headers into defaults
if attributes[:headers].present? && attributes[:headers].has_key?(:api_key) attributes[:headers] = default_headers.merge(attributes[:headers] || {})
default_headers.delete(:api_key)
end
# api_key from params hash trumps all others (headers and default_headers)
if attributes[:params].present? && attributes[:params].has_key?(:api_key)
default_headers.delete(:api_key)
attributes[:headers].delete(:api_key) if attributes[:headers].present?
end
# Merge argument headers into defaults
attributes[:headers] = default_headers.merge(attributes[:headers] || {})
# Stick in the auth token if there is one
if Swagger.authenticated?
attributes[:headers].merge!({:auth_token => Swagger.configuration.auth_token})
end
self.http_method = http_method.to_sym
self.path = path
attributes.each do |name, value|
send("#{name.to_s.underscore.to_sym}=", value)
end
end
# Construct a base URL # Stick in the auth token if there is one
# if Swagger.authenticated?
def url(options = {}) attributes[:headers].merge!({:auth_token => Swagger.configuration.auth_token})
u = Addressable::URI.new( end
:scheme => Swagger.configuration.scheme,
:host => Swagger.configuration.host,
:path => self.interpreted_path,
:query => self.query_string.sub(/\?/, '')
).to_s
# Drop trailing question mark, if present
u.sub! /\?$/, ''
# Obfuscate API key?
u.sub! /api\_key=\w+/, 'api_key=YOUR_API_KEY' if options[:obfuscated]
u
end
# Iterate over the params hash, injecting any path values into the path string self.http_method = http_method.to_sym
# self.path = path
# e.g. /word.{format}/{word}/entries => /word.json/cat/entries attributes.each do |name, value|
def interpreted_path send("#{name.to_s.underscore.to_sym}=", value)
p = self.path.dup
# Stick a .{format} placeholder into the path if there isn't
# one already or an actual format like json or xml
# e.g. /words/blah => /words.{format}/blah
if Swagger.configuration.inject_format
unless ['.json', '.xml', '{format}'].any? {|s| p.downcase.include? s }
p = p.sub(/^(\/?\w+)/, "\\1.#{format}")
end end
end end
# Stick a .{format} placeholder on the end of the path if there isn't # Construct a base URL
# one already or an actual format like json or xml def url(options = {})
# e.g. /words/blah => /words/blah.{format} u = Addressable::URI.new(
if Swagger.configuration.force_ending_format :scheme => Swagger.configuration.scheme,
unless ['.json', '.xml', '{format}'].any? {|s| p.downcase.include? s } :host => Swagger.configuration.host,
p = "#{p}.#{format}" :path => self.interpreted_path,
:query => self.query_string.sub(/\?/, '')
).to_s
# Drop trailing question mark, if present
u.sub! /\?$/, ''
# Obfuscate API key?
u.sub! /api\_key=\w+/, 'api_key=YOUR_API_KEY' if options[:obfuscated]
u
end
# Iterate over the params hash, injecting any path values into the path string
# e.g. /word.{format}/{word}/entries => /word.json/cat/entries
def interpreted_path
p = self.path.dup
# Stick a .{format} placeholder into the path if there isn't
# one already or an actual format like json or xml
# e.g. /words/blah => /words.{format}/blah
if Swagger.configuration.inject_format
unless ['.json', '.xml', '{format}'].any? {|s| p.downcase.include? s }
p = p.sub(/^(\/?\w+)/, "\\1.#{format}")
end
end
# Stick a .{format} placeholder on the end of the path if there isn't
# one already or an actual format like json or xml
# e.g. /words/blah => /words/blah.{format}
if Swagger.configuration.force_ending_format
unless ['.json', '.xml', '{format}'].any? {|s| p.downcase.include? s }
p = "#{p}.#{format}"
end
end
p = p.sub("{format}", self.format.to_s)
URI.encode [Swagger.configuration.base_path, p].join("/").gsub(/\/+/, '/')
end
# Massage the request body into a state of readiness
# If body is a hash, camelize all keys then convert to a json string
def body=(value)
if value.is_a?(Hash)
value = value.inject({}) do |memo, (k,v)|
memo[k.to_s.camelize(:lower).to_sym] = v
memo
end
end
@body = value
end
# If body is an object, JSONify it before making the actual request.
# For form parameters, remove empty value
def outgoing_body
# http form
if @body.nil? && @form_params && !@form_params.empty?
data = form_params.dup
data.each do |key, value|
data[key] = value.to_s if value && !value.is_a?(File) # remove emtpy form parameter
end
data
else # http body is JSON
@body.is_a?(String) ? @body : @body.to_json
end end
end end
p = p.sub("{format}", self.format.to_s) # Construct a query string from the query-string-type params
def query_string
URI.encode [Swagger.configuration.base_path, p].join("/").gsub(/\/+/, '/') # Iterate over all params,
end # .. removing the ones that are part of the path itself.
# .. stringifying values so Addressable doesn't blow up.
# Massage the request body into a state of readiness query_values = {}
# If body is a hash, camelize all keys then convert to a json string self.params.each_pair do |key, value|
# next if self.path.include? "{#{key}}" # skip path params
def body=(value) next if value.blank? && value.class != FalseClass # skip empties
if value.is_a?(Hash) if Swagger.configuration.camelize_params
value = value.inject({}) do |memo, (k,v)| key = key.to_s.camelize(:lower).to_sym unless key.to_sym == :api_key # api_key is not a camelCased param
memo[k.to_s.camelize(:lower).to_sym] = v end
memo query_values[key] = value.to_s
end
# We don't want to end up with '?' as our query string
# if there aren't really any params
return "" if query_values.blank?
# Addressable requires query_values to be set after initialization..
qs = Addressable::URI.new
qs.query_values = query_values
qs.to_s
end
def make
#TODO use configuration setting to determine if debugging
#logger = Logger.new STDOUT
#logger.debug self.url
response = case self.http_method.to_sym
when :get,:GET
Typhoeus::Request.get(
self.url,
:headers => self.headers.stringify_keys,
)
when :post,:POST
Typhoeus::Request.post(
self.url,
:body => self.outgoing_body,
:headers => self.headers.stringify_keys,
)
when :patch,:PATCH
Typhoeus::Request.patch(
self.url,
:body => self.outgoing_body,
:headers => self.headers.stringify_keys,
)
when :put,:PUT
Typhoeus::Request.put(
self.url,
:body => self.outgoing_body,
:headers => self.headers.stringify_keys,
)
when :delete,:DELETE
Typhoeus::Request.delete(
self.url,
:body => self.outgoing_body,
:headers => self.headers.stringify_keys,
)
end
Response.new(response)
end
def response
self.make
end
def response_code_pretty
return unless @response.present?
@response.code.to_s
end
def response_headers_pretty
return unless @response.present?
# JSON.pretty_generate(@response.headers).gsub(/\n/, '<br/>') # <- This was for RestClient
@response.headers.gsub(/\n/, '<br/>') # <- This is for Typhoeus
end
# return 'Accept' based on an array of accept provided
# @param [Array] header_accept_array Array fo 'Accept'
# @return String Accept (e.g. application/json)
def self.select_header_accept header_accept_array
if header_accept_array.empty?
return
elsif header_accept_array.any?{ |s| s.casecmp('application/json')==0 }
'application/json' # look for json data by default
else
header_accept_array.join(',')
end end
end end
@body = value
end # return the content type based on an array of content-type provided
# @param [Array] content_type_array Array fo content-type
# If body is an object, JSONify it before making the actual request. # @return String Content-Type (e.g. application/json)
# For form parameters, remove empty value def self.select_header_content_type content_type_array
def outgoing_body if content_type_array.empty?
# http form 'application/json' # use application/json by default
if @body.nil? && @form_params && !@form_params.empty? elsif content_type_array.any?{ |s| s.casecmp('application/json')==0 }
data = form_params.dup 'application/json' # use application/json if it's included
data.each do |key, value| else
data[key] = value.to_s if value && !value.is_a?(File) # remove emtpy form parameter content_type_array[0]; # otherwise, use the first one
end end
data
else # http body is JSON
@body.is_a?(String) ? @body : @body.to_json
end end
end
# Construct a query string from the query-string-type params
def query_string
# Iterate over all params, # static method to convert object (array, hash, object, etc) to JSON string
# .. removing the ones that are part of the path itself. # @param model object to be converted into JSON string
# .. stringifying values so Addressable doesn't blow up. # @return string JSON string representation of the object
query_values = {} def self.object_to_http_body model
self.params.each_pair do |key, value| return if model.nil?
next if self.path.include? "{#{key}}" # skip path params _body = nil
next if value.blank? && value.class != FalseClass # skip empties if model.is_a?(Array)
if Swagger.configuration.camelize_params _body = model.map{|m| object_to_hash(m) }
key = key.to_s.camelize(:lower).to_sym unless key.to_sym == :api_key # api_key is not a camelCased param else
_body = object_to_hash(model)
end end
query_values[key] = value.to_s _body.to_json
end end
# We don't want to end up with '?' as our query string
# if there aren't really any params
return "" if query_values.blank?
# Addressable requires query_values to be set after initialization..
qs = Addressable::URI.new
qs.query_values = query_values
qs.to_s
end
def make
#TODO use configuration setting to determine if debugging
#logger = Logger.new STDOUT
#logger.debug self.url
response = case self.http_method.to_sym
when :get,:GET
Typhoeus::Request.get(
self.url,
:headers => self.headers.stringify_keys,
)
when :post,:POST # static method to convert object(non-array) to hash
Typhoeus::Request.post( # @param obj object to be converted into JSON string
self.url, # @return string JSON string representation of the object
:body => self.outgoing_body, def self.object_to_hash obj
:headers => self.headers.stringify_keys, if obj.respond_to?(:to_hash)
) obj.to_hash
else
when :patch,:PATCH obj
Typhoeus::Request.patch( end
self.url,
:body => self.outgoing_body,
:headers => self.headers.stringify_keys,
)
when :put,:PUT
Typhoeus::Request.put(
self.url,
:body => self.outgoing_body,
:headers => self.headers.stringify_keys,
)
when :delete,:DELETE
Typhoeus::Request.delete(
self.url,
:body => self.outgoing_body,
:headers => self.headers.stringify_keys,
)
end end
Response.new(response)
end
def response
self.make
end
def response_code_pretty
return unless @response.present?
@response.code.to_s
end
def response_headers_pretty
return unless @response.present?
# JSON.pretty_generate(@response.headers).gsub(/\n/, '<br/>') # <- This was for RestClient
@response.headers.gsub(/\n/, '<br/>') # <- This is for Typhoeus
end
# return 'Accept' based on an array of accept provided
# @param [Array] header_accept_array Array fo 'Accept'
# @return String Accept (e.g. application/json)
def self.select_header_accept header_accept_array
if header_accept_array.empty?
return
elsif header_accept_array.any?{ |s| s.casecmp('application/json')==0 }
'application/json' # look for json data by default
else
header_accept_array.join(',')
end
end end
# return the content type based on an array of content-type provided
# @param [Array] content_type_array Array fo content-type
# @return String Content-Type (e.g. application/json)
def self.select_header_content_type content_type_array
if content_type_array.empty?
'application/json' # use application/json by default
elsif content_type_array.any?{ |s| s.casecmp('application/json')==0 }
'application/json' # use application/json if it's included
else
content_type_array[0]; # otherwise, use the first one
end
end
# static method to convert object (array, hash, object, etc) to JSON string
# @param model object to be converted into JSON string
# @return string JSON string representation of the object
def self.object_to_http_body model
return if model.nil?
_body = nil
if model.is_a?(Array)
_body = model.map{|m| object_to_hash(m) }
else
_body = object_to_hash(model)
end
_body.to_json
end
# static method to convert object(non-array) to hash
# @param obj object to be converted into JSON string
# @return string JSON string representation of the object
def self.object_to_hash obj
if obj.respond_to?(:to_hash)
obj.to_hash
else
obj
end
end
end end
end end

View File

@ -1,70 +1,70 @@
module Swagger module {{moduleName}}
module Swagger
class Response
require 'json'
class Response attr_accessor :raw
require 'json'
attr_accessor :raw def initialize(raw)
self.raw = raw
def initialize(raw) case self.code
self.raw = raw when 500..510 then raise(ServerError, self.error_message)
when 299..426 then raise(ClientError, self.error_message)
end
end
case self.code def code
when 500..510 then raise(ServerError, self.error_message) raw.code
when 299..426 then raise(ClientError, self.error_message) end
# Account for error messages that take different forms...
def error_message
body['message']
rescue
body
end
# If body is JSON, parse it
# Otherwise return raw string
def body
JSON.parse(raw.body, :symbolize_names => true)
rescue
raw.body
end
# `headers_hash` is a Typhoeus-specific extension of Hash,
# so simplify it back into a regular old Hash.
def headers
h = {}
raw.headers_hash.each {|k,v| h[k] = v }
h
end
# Extract the response format from the header hash
# e.g. {'Content-Type' => 'application/json'}
def format
headers['Content-Type'].split("/").last.downcase
end
def json?
format == 'json'
end
def xml?
format == 'xml'
end
def pretty_body
return unless body.present?
case format
when 'json' then JSON.pretty_generate(body).gsub(/\n/, '<br/>')
end
end
def pretty_headers
JSON.pretty_generate(headers).gsub(/\n/, '<br/>')
end end
end end
def code
raw.code
end
# Account for error messages that take different forms...
def error_message
body['message']
rescue
body
end
# If body is JSON, parse it
# Otherwise return raw string
def body
JSON.parse(raw.body, :symbolize_names => true)
rescue
raw.body
end
# `headers_hash` is a Typhoeus-specific extension of Hash,
# so simplify it back into a regular old Hash.
def headers
h = {}
raw.headers_hash.each {|k,v| h[k] = v }
h
end
# Extract the response format from the header hash
# e.g. {'Content-Type' => 'application/json'}
def format
headers['Content-Type'].split("/").last.downcase
end
def json?
format == 'json'
end
def xml?
format == 'xml'
end
def pretty_body
return unless body.present?
case format
when 'json' then JSON.pretty_generate(body).gsub(/\n/, '<br/>')
end
end
def pretty_headers
JSON.pretty_generate(headers).gsub(/\n/, '<br/>')
end
end end
end end

View File

@ -1,4 +1,5 @@
module Swagger module {{moduleName}}
VERSION = "4.06.08" module Swagger
VERSION = "{{appVersion}}"
end
end end

View File

@ -0,0 +1,32 @@
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "{{gemName}}/swagger/version"
Gem::Specification.new do |s|
s.name = "{{gemName}}"
s.version = {{moduleName}}::Swagger::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ["Zeke Sikelianos", "Tony Tam"]
s.email = ["zeke@wordnik.com", "tony@wordnik.com"]
s.homepage = "http://developer.wordnik.com"
s.summary = %q{A ruby wrapper for the swagger APIs}
s.description = %q{This gem maps to a swagger API}
s.license = "Apache-2.0"
s.add_runtime_dependency 'typhoeus', '~> 0.2', '>= 0.2.1'
s.add_runtime_dependency 'addressable', '~> 2.2', '>= 2.2.4'
s.add_runtime_dependency 'json', '~> 1.4', '>= 1.4.6'
s.add_development_dependency 'rspec', '~> 3.2', '>= 3.2.0'
s.add_development_dependency 'vcr', '~> 2.9', '>= 2.9.3'
s.add_development_dependency 'webmock', '~> 1.6', '>= 1.6.2'
s.add_development_dependency 'autotest', '~> 4.4', '>= 4.4.6'
s.add_development_dependency 'autotest-rails-pure', '~> 4.1', '>= 4.1.2'
s.add_development_dependency 'autotest-growl', '~> 0.2', '>= 0.2.16'
s.add_development_dependency 'autotest-fsevent', '~> 0.2', '>= 0.2.10'
s.files = `find *`.split("\n").uniq.sort.select{|f| !f.empty? }
s.test_files = `find spec/*`.split("\n")
s.executables = []
s.require_paths = ["lib"]
end

View File

@ -0,0 +1,25 @@
# Swagger common files
require '{{gemName}}/monkey'
require '{{gemName}}/swagger'
require '{{gemName}}/swagger/configuration'
require '{{gemName}}/swagger/request'
require '{{gemName}}/swagger/response'
require '{{gemName}}/swagger/version'
# Models
require '{{modelPackage}}/base_object'
{{#models}}
require '{{importPath}}'
{{/models}}
# APIs
{{#apiInfo}}
{{#apis}}
require '{{importPath}}'
{{/apis}}
{{/apiInfo}}
module {{moduleName}}
# Initialize the default configuration
Swagger.configuration ||= Swagger::Configuration.new
end

View File

@ -720,6 +720,18 @@
"description": "successful operation", "description": "successful operation",
"schema": { "schema": {
"$ref": "#/definitions/User" "$ref": "#/definitions/User"
},
"examples": {
"application/json": {
"id": 1,
"username": "johnp",
"firstName": "John",
"lastName": "Public",
"email": "johnp@swagger.io",
"password": "-secret-",
"phone": "0123456789",
"userStatus": 0
}
} }
}, },
"400": { "400": {

View File

@ -0,0 +1,93 @@
{
"swagger": "2.0",
"info": {
"description": "This is a sample server Petstore server. You can find out more about Swagger at <a href=\"http://swagger.io\">http://swagger.io</a> or on irc.freenode.net, #swagger. For this sample, you can use the api key \"special-key\" to test the authorization filters",
"version": "1.0.0",
"title": "Swagger Petstore",
"termsOfService": "http://helloreverb.com/terms/",
"contact": {
"email": "apiteam@wordnik.com"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
},
"host": "petstore.swagger.io",
"basePath": "/v2",
"schemes": [
"http"
],
"paths": {
"/tests/requiredParams": {
"get": {
"tags": [
"tests"
],
"summary": "Operation with required parameters",
"description": "",
"operationId": "requiredParams",
"produces": [
"application/json"
],
"parameters": [
{
"name": "param1",
"in": "formData",
"description": "Some required parameter",
"required": true,
"type": "integer",
"format": "int64"
},
{
"name": "param2",
"in": "formData",
"description": "Some optional parameter",
"required": false,
"type": "string"
}
],
"responses": {
"200": {
"description": "successful operation. Retuning a simple int.",
"schema": {
"type": "integer",
"format": "int64"
}
}
}
}
}
},
"securityDefinitions": {
"api_key": {
"type": "apiKey",
"name": "api_key",
"in": "header"
},
"petstore_auth": {
"type": "oauth2",
"authorizationUrl": "http://petstore.swagger.io/api/oauth/dialog",
"flow": "implicit",
"scopes": {
"write:pets": "modify pets in your account",
"read:pets": "read your pets"
}
}
},
"definitions": {
"CustomModel": {
"required": ["id"],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string",
"example": "doggie"
}
}
}
}
}

View File

@ -0,0 +1,316 @@
{
"swaggerVersion": "1.2",
"apiVersion": "v1beta3",
"basePath": "https://127.0.0.1:6443",
"resourcePath": "/api/v1beta3",
"apis": [
{
"path": "/api/v1beta3/namespaces/{namespaces}/bindings",
"description": "API at /api/v1beta3 version v1beta3",
"operations": [
{
"type": "v1beta3.Binding",
"method": "POST",
"summary": "create a Binding",
"nickname": "createBinding",
"parameters": [
{
"type": "string",
"paramType": "path",
"name": "namespaces",
"description": "object name and auth scope, such as for teams and projects",
"required": true,
"allowMultiple": false
},
{
"type": "v1beta3.Binding",
"paramType": "body",
"name": "body",
"description": "",
"required": true,
"allowMultiple": false
}
],
"responseMessages": [
{
"code": 200,
"message": "OK",
"responseModel": "v1beta3.Binding"
}
],
"produces": [
"application/json"
],
"consumes": [
"*/*"
]
}
]
},
{
"path": "/api/v1beta3/namespaces/{namespaces}/componentstatuses",
"description": "API at /api/v1beta3 version v1beta3",
"operations": [
{
"type": "v1beta3.ComponentStatusList",
"method": "GET",
"summary": "list objects of kind ComponentStatus",
"nickname": "listComponentStatus",
"parameters": [
{
"type": "string",
"paramType": "path",
"name": "namespaces",
"description": "object name and auth scope, such as for teams and projects",
"required": true,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "fieldSelector",
"description": "a selector to restrict the list of returned objects by their fields; defaults to everything",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "labelSelector",
"description": "a selector to restrict the list of returned objects by their labels; defaults to everything",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "resourceVersion",
"description": "when specified with a watch call, shows changes that occur after that particular version of a resource; defaults to changes from the beginning of history",
"required": false,
"allowMultiple": false
},
{
"type": "boolean",
"paramType": "query",
"name": "watch",
"description": "watch for changes to the described resources and return them as a stream of add, update, and remove notifications; specify resourceVersion",
"required": false,
"allowMultiple": false
}
],
"responseMessages": [
{
"code": 200,
"message": "OK",
"responseModel": "v1beta3.ComponentStatusList"
}
],
"produces": [
"application/json"
],
"consumes": [
"*/*"
]
}
]
},
{
"path": "/api/v1beta3/namespaces/{namespaces}/componentstatuses/{name}",
"description": "API at /api/v1beta3 version v1beta3",
"operations": [
{
"type": "v1beta3.ComponentStatus",
"method": "GET",
"summary": "read the specified ComponentStatus",
"nickname": "readComponentStatus",
"parameters": [
{
"type": "string",
"paramType": "path",
"name": "name",
"description": "name of the ComponentStatus",
"required": true,
"allowMultiple": false
},
{
"type": "string",
"paramType": "path",
"name": "namespaces",
"description": "object name and auth scope, such as for teams and projects",
"required": true,
"allowMultiple": false
}
],
"responseMessages": [
{
"code": 200,
"message": "OK",
"responseModel": "v1beta3.ComponentStatus"
}
],
"produces": [
"application/json"
],
"consumes": [
"*/*"
]
}
]
}
],
"models": {
"v1beta3.ComponentStatus": {
"id": "v1beta3.ComponentStatus",
"properties": {
"apiVersion": {
"type": "string",
"description": "version of the schema the object should have"
},
"conditions": {
"type": "array",
"items": {
"$ref": "v1beta3.ObjectReference"
},
"description": "list of component conditions observed"
},
"kind": {
"type": "string",
"description": "kind of object, in CamelCase; cannot be updated"
},
"metadata": {
"$ref": "v1beta3.ObjectMeta",
"description": "standard object metadata; see http://docs.k8s.io/api-conventions.md#metadata"
}
}
},
"v1beta3.ComponentStatusList": {
"id": "v1beta3.ComponentStatusList",
"required": [
"items"
],
"properties": {
"apiVersion": {
"type": "string",
"description": "version of the schema the object should have"
},
"items": {
"type": "array",
"items": {
"$ref": "v1beta3.ComponentStatus"
},
"description": "list of component status objects"
},
"kind": {
"type": "string",
"description": "kind of object, in CamelCase; cannot be updated"
},
"metadata": {
"$ref": "v1beta3.ObjectMeta",
"description": "standard list metadata; see http://docs.k8s.io/api-conventions.md#metadata"
}
}
},
"v1beta3.Binding": {
"id": "v1beta3.Binding",
"required": [
"target"
],
"properties": {
"apiVersion": {
"type": "string",
"description": "version of the schema the object should have"
},
"kind": {
"type": "string",
"description": "kind of object, in CamelCase; cannot be updated"
},
"metadata": {
"$ref": "v1beta3.ObjectMeta",
"description": "standard object metadata; see http://docs.k8s.io/api-conventions.md#metadata"
},
"target": {
"$ref": "v1beta3.ObjectReference",
"description": "an object to bind to"
}
}
},
"v1beta3.ObjectReference": {
"id": "v1beta3.ObjectReference",
"properties": {
"apiVersion": {
"type": "string",
"description": "API version of the referent"
},
"fieldPath": {
"type": "string",
"description": "if referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]"
},
"kind": {
"type": "string",
"description": "kind of the referent"
},
"name": {
"type": "string",
"description": "name of the referent"
},
"namespace": {
"type": "string",
"description": "namespace of the referent"
},
"resourceVersion": {
"type": "string",
"description": "specific resourceVersion to which this reference is made, if any: http://docs.k8s.io/api-conventions.md#concurrency-control-and-consistency"
},
"uid": {
"type": "string",
"description": "uid of the referent"
}
}
},
"v1beta3.ObjectMeta": {
"id": "v1beta3.ObjectMeta",
"properties": {
"annotations": {
"type": "any",
"description": "map of string keys and values that can be used by external tooling to store and retrieve arbitrary metadata about objects"
},
"creationTimestamp": {
"type": "string",
"description": "RFC 3339 date and time at which the object was created; populated by the system, read-only; null for lists"
},
"deletionTimestamp": {
"type": "string",
"description": "RFC 3339 date and time at which the object will be deleted; populated by the system when a graceful deletion is requested, read-only; if not set, graceful deletion of the object has not been requested"
},
"generateName": {
"type": "string",
"description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified"
},
"labels": {
"type": "any",
"description": "map of string keys and values that can be used to organize and categorize objects; may match selectors of replication controllers and services"
},
"name": {
"type": "string",
"description": "string that identifies an object. Must be unique within a namespace; cannot be updated"
},
"namespace": {
"type": "string",
"description": "namespace of the object; cannot be updated"
},
"resourceVersion": {
"type": "string",
"description": "string that identifies the internal version of this object that can be used by clients to determine when objects have changed; populated by the system, read-only; value must be treated as opaque by clients and passed unmodified back to the server: http://docs.k8s.io/api-conventions.md#concurrency-control-and-consistency"
},
"selfLink": {
"type": "string",
"description": "URL for the object; populated by the system, read-only"
},
"uid": {
"type": "string",
"description": "unique UUID across space and time; populated by the system; read-only"
}
}
}
}
}

View File

@ -1,15 +1,9 @@
import com.wordnik.swagger.models._
import com.wordnik.swagger.util.Json
import io.swagger.parser._
import com.wordnik.swagger.codegen.DefaultCodegen import com.wordnik.swagger.codegen.DefaultCodegen
import com.wordnik.swagger.models.properties.Property
import io.swagger.parser._
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner import org.scalatest.junit.JUnitRunner
import org.scalatest.FlatSpec import org.scalatest.{FlatSpec, Matchers}
import org.scalatest.Matchers
import scala.collection.JavaConverters._
@RunWith(classOf[JUnitRunner]) @RunWith(classOf[JUnitRunner])
class CodegenTest extends FlatSpec with Matchers { class CodegenTest extends FlatSpec with Matchers {
@ -95,6 +89,32 @@ class CodegenTest extends FlatSpec with Matchers {
statusParam.hasMore should be (null) statusParam.hasMore should be (null)
} }
it should "handle required parameters from a 2.0 spec as required when figuring out Swagger types" in {
val model = new SwaggerParser()
.read("src/test/resources/2_0/requiredTest.json")
val codegen = new DefaultCodegen() {
override def getSwaggerType(p: Property) = Option(p) match {
case Some(property) if !property.getRequired =>
"Optional<" + super.getSwaggerType(p) + ">"
case other => super.getSwaggerType(p)
}
}
val path = "/tests/requiredParams"
val p = model.getPaths().get(path).getGet()
val op = codegen.fromOperation(path, "get", p, model.getDefinitions)
val formParams = op.formParams
formParams.size should be(2)
val requiredParam = formParams.get(0)
requiredParam.dataType should be("Long")
val optionalParam = formParams.get(1)
optionalParam.dataType should be("Optional<string>")
op.returnType should be("Long")
}
it should "select main response from a 2.0 spec using the lowest 2XX code" in { it should "select main response from a 2.0 spec using the lowest 2XX code" in {
val model = new SwaggerParser() val model = new SwaggerParser()
.read("src/test/resources/2_0/responseSelectionTest.json") .read("src/test/resources/2_0/responseSelectionTest.json")

View File

@ -0,0 +1,67 @@
import scala.collection.JavaConverters.asScalaBufferConverter
import scala.collection.JavaConverters.mapAsJavaMapConverter
import scala.collection.JavaConverters.seqAsJavaListConverter
import org.junit.runner.RunWith
import org.scalatest.FlatSpec
import org.scalatest.Matchers
import org.scalatest.junit.JUnitRunner
import com.wordnik.swagger.codegen.examples.ExampleGenerator
import com.wordnik.swagger.models.Model
import com.wordnik.swagger.models.ModelImpl
import com.wordnik.swagger.models.Xml
import com.wordnik.swagger.models.properties.ArrayProperty
import com.wordnik.swagger.models.properties.RefProperty
import com.wordnik.swagger.models.properties.StringProperty
@RunWith(classOf[JUnitRunner])
class ExampleGeneratorTest extends FlatSpec with Matchers {
val json = "application/json"
val xml = "application/xml"
it should "check handling of recursive models" in {
val nodeType = "Node"
val ref = new RefProperty(nodeType)
val node = new ModelImpl().name(nodeType).property("name", new StringProperty())
node.property("parent", ref)
node.property("children", new ArrayProperty(ref))
node.property("wrappedChildren", new ArrayProperty(ref).xml(new Xml().wrapped(true)))
val pairType = "Pair"
val pair = new ModelImpl().name(pairType)
for (item <- Map("first" -> "First", "second" -> "Second")) {
val property = new RefProperty(nodeType)
property.setXml(new Xml().name(item._2))
pair.property(item._1, property);
}
val types = scala.collection.mutable.Buffer[String]()
val expectedTypes = List(json, xml)
val eg = new ExampleGenerator(Map[String, Model](nodeType -> node, pairType -> pair).asJava)
for (item <- eg.generate(null, expectedTypes.asJava, new RefProperty(pairType)).asScala) {
val example = item.get("example")
item.get("contentType") match {
case `xml` => {
types += xml
example should be ("<Pair>\n" +
" <Node>\n" +
" <name>string</name>\n" +
" <wrappedChildren>\n" +
" </wrappedChildren>\n" +
" </Node>\n" +
" <Node>\n" +
" <name>string</name>\n" +
" <wrappedChildren>\n" +
" </wrappedChildren>\n" +
" </Node>\n" +
"</Pair>")
}
case `json` => {
types += json
// TODO - add JSON validation
example should not be (null)
}
}
}
types should be (expectedTypes)
}
}

View File

@ -0,0 +1,34 @@
package python
import com.wordnik.swagger.codegen.languages.{PythonClientCodegen}
import io.swagger.parser.SwaggerParser
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.FlatSpec
import org.scalatest.Matchers
@RunWith(classOf[JUnitRunner])
class PythonTest extends FlatSpec with Matchers {
it should "convert a python model with dots" in {
val swagger = new SwaggerParser()
.read("src/test/resources/2_0/v1beta3.json")
val codegen = new PythonClientCodegen()
val simpleName = codegen.fromModel("v1beta3.Binding", swagger.getDefinitions().get("v1beta3.Binding"))
simpleName.name should be("v1beta3.Binding")
simpleName.classname should be("V1beta3Binding")
simpleName.classVarName should be("v1beta3_binding")
val compoundName = codegen.fromModel("v1beta3.ComponentStatus", swagger.getDefinitions().get("v1beta3.ComponentStatus"))
compoundName.name should be("v1beta3.ComponentStatus")
compoundName.classname should be("V1beta3ComponentStatus")
compoundName.classVarName should be("v1beta3_component_status")
val path = "/api/v1beta3/namespaces/{namespaces}/bindings"
val operation = swagger.getPaths().get(path).getPost()
val codegenOperation = codegen.fromOperation(path, "get", operation, swagger.getDefinitions())
codegenOperation.returnType should be("V1beta3Binding")
codegenOperation.returnBaseType should be("V1beta3Binding")
}
}

View File

@ -6,11 +6,9 @@
<version>2.1.1-M2-SNAPSHOT</version> <version>2.1.1-M2-SNAPSHOT</version>
<relativePath>../..</relativePath> <relativePath>../..</relativePath>
</parent> </parent>
<groupId>com.wordnik</groupId>
<artifactId>swagger-generator</artifactId> <artifactId>swagger-generator</artifactId>
<packaging>war</packaging> <packaging>war</packaging>
<name>swagger-generator</name> <name>swagger-generator</name>
<version>2.1.1-M2-SNAPSHOT</version>
<build> <build>
<sourceDirectory>src/main/java</sourceDirectory> <sourceDirectory>src/main/java</sourceDirectory>
<resources> <resources>

View File

@ -450,10 +450,10 @@
</repository> </repository>
</repositories> </repositories>
<properties> <properties>
<swagger-parser-version>1.0.6-SNAPSHOT</swagger-parser-version> <swagger-parser-version>1.0.7</swagger-parser-version>
<scala-version>2.10.4</scala-version> <scala-version>2.10.4</scala-version>
<felix-version>2.3.4</felix-version> <felix-version>2.3.4</felix-version>
<swagger-core-version>1.5.1-M2</swagger-core-version> <swagger-core-version>1.5.2-M2</swagger-core-version>
<scala-test-version>2.1.4</scala-test-version> <scala-test-version>2.1.4</scala-test-version>
<commons-io-version>2.3</commons-io-version> <commons-io-version>2.3</commons-io-version>
<commons-cli-version>1.2</commons-cli-version> <commons-cli-version>1.2</commons-cli-version>

View File

@ -0,0 +1,227 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wordnik</groupId>
<artifactId>swagger-client</artifactId>
<packaging>jar</packaging>
<name>swagger-client</name>
<version>1.0.0</version>
<prerequisites>
<maven>2.2.0</maven>
</prerequisites>
<pluginRepositories>
<pluginRepository>
<id>maven-mongodb-plugin-repo</id>
<name>maven mongodb plugin repository</name>
<url>http://maven-mongodb-plugin.googlecode.com/svn/maven/repo</url>
<layout>default</layout>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<configuration>
<systemProperties>
<property>
<name>loggerPath</name>
<value>conf/log4j.properties</value>
</property>
</systemProperties>
<argLine>-Xms512m -Xmx1500m</argLine>
<parallel>methods</parallel>
<forkMode>pertest</forkMode>
</configuration>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!-- attach test jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add_sources</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>
src/main/java</source>
</sources>
</configuration>
</execution>
<execution>
<id>add_test_sources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>
src/test/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>
1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>${scala-maven-plugin-version}</version>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>scala-test-compile</id>
<phase>process-test-resources</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<args>
<arg>-feature</arg>
</args>
<jvmArgs>
<jvmArg>-Xms128m</jvmArg>
<jvmArg>-Xmx1500m</jvmArg>
</jvmArgs>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<configuration>
<scalaVersion>${scala-version}</scalaVersion>
</configuration>
</plugin>
</plugins>
</reporting>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala-version}</version>
</dependency>
<dependency>
<groupId>com.wordnik</groupId>
<artifactId>swagger-core</artifactId>
<version>${swagger-core-version}</version>
</dependency>
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest_2.10</artifactId>
<version>${scala-test-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${joda-time-version}</version>
</dependency>
<dependency>
<groupId>org.joda</groupId>
<artifactId>joda-convert</artifactId>
<version>${joda-version}</version>
</dependency>
<dependency>
<groupId>com.typesafe</groupId>
<artifactId>config</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.10</artifactId>
<version>${akka-version}</version>
</dependency>
<dependency>
<groupId>io.spray</groupId>
<artifactId>spray-client</artifactId>
<version>${spray-version}</version>
</dependency>
<dependency>
<groupId>org.json4s</groupId>
<artifactId>json4s-jackson_2.10</artifactId>
<version>${json4s-jackson-version}</version>
</dependency>
</dependencies>
<properties>
<scala-version>2.10.4</scala-version>
<json4s-jackson-version>3.2.11</json4s-jackson-version>
<json4s-ext-version>3.2.11</json4s-ext-version>
<spray-version>1.3.1</spray-version>
<akka-version>2.3.9</akka-version>
<joda-version>1.2</joda-version>
<joda-time-version>2.2</joda-time-version>
<swagger-core-version>1.5.0-M1</swagger-core-version>
<maven-plugin.version>1.0.0</maven-plugin.version>
<junit-version>4.8.1</junit-version>
<scala-maven-plugin-version>3.1.5</scala-maven-plugin-version>
<scala-test-version>2.1.3</scala-test-version>
</properties>
</project>

Some files were not shown because too many files have changed in this diff Show More