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/scalatra/target
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
.idea
.lib

View File

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

View File

@ -26,6 +26,6 @@ fi
# 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"
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

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")
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
public void run() {
verbosed(verbose);
setSystemProperties();
ClientOptInput input = new ClientOptInput();
if (isNotEmpty(auth)) {
@ -77,6 +83,17 @@ public class Generate implements Runnable {
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
* @param verbose - if true, enables debug mode

View File

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

View File

@ -56,4 +56,8 @@ public interface CodegenConfig {
Map<String, Object> postProcessModels(Map<String, Object> objs);
Map<String, Object> postProcessOperations(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 List<CodegenProperty> vars = new ArrayList<CodegenProperty>();
public Set<String> imports = new HashSet<String>();
public Boolean hasVars, emptyVars, hasMoreModels;
public Boolean hasVars, emptyVars, hasMoreModels, hasEnums;
public ExternalDocs externalDocs;
}

View File

@ -3,7 +3,7 @@ package com.wordnik.swagger.codegen;
public class CodegenParameter {
public Boolean isFormParam, isQueryParam, isPathParam, isHeaderParam,
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;
/**
@ -34,7 +34,8 @@ public class CodegenParameter {
output.isBodyParam = this.isBodyParam;
output.required = this.required;
output.jsonSchema = this.jsonSchema;
output.defaultValue = this.defaultValue;
return output;
}
}
}

View File

@ -16,7 +16,10 @@ public class CodegenProperty {
public String example;
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 isPrimitiveType, isContainer, isNotContainer;
public boolean isEnum;

View File

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

View File

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

View File

@ -1,26 +1,58 @@
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.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.BasicAuthDefinition;
import com.wordnik.swagger.models.auth.In;
import com.wordnik.swagger.models.auth.SecuritySchemeDefinition;
import com.wordnik.swagger.models.parameters.*;
import com.wordnik.swagger.models.properties.*;
import com.wordnik.swagger.models.parameters.BodyParameter;
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 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 {
Logger LOGGER = LoggerFactory.getLogger(DefaultCodegen.class);
@ -166,6 +198,9 @@ public class DefaultCodegen {
return name;
}
public String toEnumName(CodegenProperty property) {
return StringUtils.capitalize(property.name) + "Enum";
}
public String escapeReservedWord(String name) {
throw new RuntimeException("reserved word " + name + " not allowed");
@ -470,6 +505,7 @@ public class DefaultCodegen {
}
if(impl.getProperties() != null && impl.getProperties().size() > 0) {
m.hasVars = true;
m.hasEnums = false;
for(String key: impl.getProperties().keySet()) {
Property prop = impl.getProperties().get(key);
@ -497,6 +533,8 @@ public class DefaultCodegen {
}
m.vars.add(cp);
count += 1;
if (cp.isEnum)
m.hasEnums = true;
if(count != impl.getProperties().keySet().size())
cp.hasMore = new Boolean(true);
if(cp.isContainer != null) {
@ -589,7 +627,7 @@ public class DefaultCodegen {
// this can cause issues for clients which don't support enums
if(property.isEnum)
property.datatypeWithEnum = StringUtils.capitalize(property.name) + "Enum";
property.datatypeWithEnum = toEnumName(property);
else
property.datatypeWithEnum = property.datatype;
@ -692,11 +730,11 @@ public class DefaultCodegen {
for(String key: operation.getConsumes()) {
Map<String, String> mediaType = new HashMap<String, String>();
mediaType.put("mediaType", key);
count += 1;
if (count < operation.getConsumes().size())
mediaType.put("hasMore", "true");
else
mediaType.put("hasMore", null);
count += 1;
c.add(mediaType);
}
op.consumes = c;
@ -722,7 +760,6 @@ public class DefaultCodegen {
if (operation.getResponses() != null && !operation.getResponses().isEmpty()) {
Response methodResponse = findMethodResponse(operation.getResponses());
CodegenResponse methodCodegenResponse = null;
for (Map.Entry<String, Response> entry : operation.getResponses().entrySet()) {
Response response = entry.getValue();
@ -732,9 +769,7 @@ public class DefaultCodegen {
!defaultIncludes.contains(r.baseType) &&
!languageSpecificPrimitives.contains(r.baseType))
imports.add(r.baseType);
if (response == methodResponse)
methodCodegenResponse = r;
r.isDefault = response == methodResponse;
op.responses.add(r);
}
op.responses.get(op.responses.size() - 1).hasMore = false;
@ -904,6 +939,15 @@ public class DefaultCodegen {
p.required = param.getRequired();
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) {
SerializableParameter qp = (SerializableParameter) param;
Property property = null;
@ -918,6 +962,19 @@ public class DefaultCodegen {
collectionFormat = qp.getCollectionFormat();
CodegenProperty pr = fromProperty("inner", inner);
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);
}
else
@ -926,6 +983,7 @@ public class DefaultCodegen {
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.setRequired(param.getRequired());
CodegenProperty model = fromProperty(qp.getName(), property);
p.collectionFormat = collectionFormat;
p.dataType = model.datatype;
@ -949,6 +1007,7 @@ public class DefaultCodegen {
else {
// TODO: missing format, so this will not always work
Property prop = PropertyBuilder.build(impl.getType(), null, null);
prop.setRequired(bp.getRequired());
CodegenProperty cp = fromProperty("property", prop);
if(cp != null) {
p.dataType = cp.datatype;
@ -962,6 +1021,7 @@ public class DefaultCodegen {
CodegenModel cm = fromModel(bp.getName(), impl);
// get the single property
ArrayProperty ap = new ArrayProperty().items(impl.getItems());
ap.setRequired(param.getRequired());
CodegenProperty cp = fromProperty("inner", ap);
if(cp.complexType != null) {
imports.add(cp.complexType);
@ -996,9 +1056,9 @@ public class DefaultCodegen {
if(schemes == null)
return null;
List<CodegenSecurity> secs = new ArrayList<CodegenSecurity>();
for(Iterator entries = schemes.entrySet().iterator(); entries.hasNext(); ) {
Map.Entry<String, SecuritySchemeDefinition> entry = (Map.Entry<String, SecuritySchemeDefinition>) entries.next();
List<CodegenSecurity> secs = new ArrayList<CodegenSecurity>(schemes.size());
for (Iterator<Map.Entry<String, SecuritySchemeDefinition>> it = schemes.entrySet().iterator(); it.hasNext();) {
final Map.Entry<String, SecuritySchemeDefinition> entry = it.next();
final SecuritySchemeDefinition schemeDefinition = entry.getValue();
CodegenSecurity sec = CodegenModelFactory.newInstance(CodegenModelType.SECURITY);
@ -1018,23 +1078,21 @@ public class DefaultCodegen {
sec.isOAuth = !sec.isBasic;
}
sec.hasMore = entries.hasNext();
sec.hasMore = it.hasNext();
secs.add(sec);
}
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)
return null;
List<Map<String, String>> output = new ArrayList<Map<String, String>>();
for(String key: examples.keySet()) {
String value = examples.get(key);
Map<String, String> kv = new HashMap<String, String>();
kv.put("contentType", key);
kv.put("example", value);
final List<Map<String, Object>> output = new ArrayList<Map<String, Object>>(examples.size());
for(Map.Entry<String, Object> entry : examples.entrySet()) {
final Map<String, Object> kv = new HashMap<String, Object>();
kv.put("contentType", entry.getKey());
kv.put("example", entry.getValue());
output.add(kv);
}
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.put("classname", config.toApiName(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));
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()) {
String suffix = config.apiTemplateFiles().get(templateName);
String filename = config.apiFileFolder() +
File.separator +
config.toApiFilename(tag) +
suffix;
String filename = config.apiFilename( templateName, tag );
if( new File( filename ).exists() && !config.shouldOverwrite( filename )){
continue;
}
String template = readTemplate(config.templateDir() + File.separator + templateName);
Template tmpl = Mustache.compiler()
@ -217,6 +221,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
bundle.put("models", allModels);
bundle.put("apiFolder", config.apiPackage().replace('.', File.separatorChar));
bundle.put("modelPackage", config.modelPackage());
bundle.put("authMethods", config.fromSecurity(swagger.getSecurityDefinitions()));
if (swagger.getExternalDocs() != null) {
bundle.put("externalDocs", swagger.getExternalDocs());
}
@ -291,6 +296,28 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
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) {
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);
Map<String, Object> mo = new HashMap<String, Object>();
mo.put("model", cm);
mo.put("importPath", config.toModelImport(key));
models.add(mo);
allImports.addAll(cm.imports);
}

View File

@ -1,19 +1,33 @@
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.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 {
protected Map<String, Model> examples;
@ -22,7 +36,7 @@ public class ExampleGenerator {
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>>();
Set<String> processedModels = new HashSet<String>();
if(examples == null ) {
@ -37,7 +51,6 @@ public class ExampleGenerator {
String example = Json.pretty(resolvePropertyToExample(mediaType, property, processedModels));
if(example != null) {
example = example.replaceAll("\n", "\\\\n");
kv.put("example", example);
output.add(kv);
}
@ -45,7 +58,6 @@ public class ExampleGenerator {
else if(property != null && mediaType.startsWith("application/xml")) {
String example = new XmlExampleGenerator(this.examples).toXml(property);
if(example != null) {
example = example.replaceAll("\n", "\\\\n");
kv.put("example", example);
output.add(kv);
}
@ -53,12 +65,10 @@ public class ExampleGenerator {
}
}
else {
for(String key: examples.keySet()) {
String value = examples.get(key);
Map<String, String> kv = new HashMap<String, String>();
kv.put("contentType", key);
kv.put("example", value);
for(Map.Entry<String, Object> entry: examples.entrySet()) {
final Map<String, String> kv = new HashMap<String, String>();
kv.put("contentType", entry.getKey());
kv.put("example", Json.pretty(entry.getValue()));
output.add(kv);
}
}

View File

@ -8,6 +8,8 @@ import com.wordnik.swagger.models.properties.*;
import java.text.SimpleDateFormat;
import java.util.*;
import org.codehaus.plexus.util.StringUtils;
public class XmlExampleGenerator {
public static String NEWLINE = "\n";
public static String TAG_START = "<";
@ -16,6 +18,7 @@ public class XmlExampleGenerator {
protected Map<String, Model> examples;
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'");
private static String EMPTY = "";
public XmlExampleGenerator(Map<String, Model> examples) {
this.examples = examples;
@ -24,29 +27,36 @@ public class XmlExampleGenerator {
}
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) {
RefModel ref = (RefModel) model;
Model actualModel = examples.get(ref.getSimpleRef());
if(actualModel instanceof ModelImpl)
return modelImplToXml((ModelImpl)actualModel, indent);
return modelImplToXml((ModelImpl) actualModel, indent, path);
}
else if(model instanceof ModelImpl) {
return modelImplToXml((ModelImpl)model, indent);
return modelImplToXml((ModelImpl) model, indent, path);
}
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();
// attributes
Map<String, Property> attributes = new LinkedHashMap<String, Property>();
Map<String, Property> elements = new LinkedHashMap<String, Property>();
String name = model.getName();
String name = modelName;
String namespace;
String prefix;
Boolean wrapped;
@ -67,13 +77,17 @@ public class XmlExampleGenerator {
sb.append(name);
for(String pName : attributes.keySet()) {
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(NEWLINE);
for(String pName : elements.keySet()) {
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(indent(indent)).append(TAG_END).append(name).append(CLOSE_TAG);
@ -85,7 +99,7 @@ public class XmlExampleGenerator {
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) {
return "";
}
@ -98,12 +112,16 @@ public class XmlExampleGenerator {
if(property.getXml() != null && property.getXml().getWrapped())
wrapped = true;
if(wrapped) {
String prefix = EMPTY;
if(name != null) {
sb.append(indent(indent));
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) {
sb.append(NEWLINE);
sb.append(indent(indent));
@ -111,12 +129,12 @@ public class XmlExampleGenerator {
}
}
else
sb.append(toXml(name, inner, indent));
sb.append(toXml(name, inner, indent, path));
}
else if(property instanceof RefProperty) {
RefProperty ref = (RefProperty) property;
Model actualModel = examples.get(ref.getSimpleRef());
sb.append(toXml(actualModel, indent));
sb.append(toXml(actualModel, indent, path));
}
else {
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 projectFolder = "src/main";
protected String sourceFolder = projectFolder + "/java";
protected Boolean useAndroidMavenGradlePlugin = true;
public CodegenType getTag() {
return CodegenType.CLIENT;
@ -52,6 +53,9 @@ public class AndroidClientCodegen extends DefaultCodegen implements CodegenConfi
additionalProperties.put("artifactVersion", artifactVersion);
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("manifest.mustache", projectFolder, "AndroidManifest.xml"));
supportingFiles.add(new SupportingFile("apiInvoker.mustache",

View File

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

View File

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

View File

@ -1,8 +1,6 @@
package com.wordnik.swagger.codegen.languages;
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.models.properties.*;
@ -14,7 +12,6 @@ public class JaxRSServerCodegen extends JavaClientCodegen implements CodegenConf
protected String groupId = "io.swagger";
protected String artifactId = "swagger-jaxrs-server";
protected String artifactVersion = "1.0.0";
protected String sourceFolder = "src/main/java";
protected String title = "Swagger Server";
public CodegenType getTag() {
@ -31,12 +28,19 @@ public class JaxRSServerCodegen extends JavaClientCodegen implements CodegenConf
public JaxRSServerCodegen() {
super();
outputFolder = "generated-code/javaJaxRS";
sourceFolder = "src/gen/java";
outputFolder = System.getProperty( "swagger.codegen.jaxrs.genfolder", "generated-code/javaJaxRS" );
modelTemplateFiles.put("model.mustache", ".java");
apiTemplateFiles.put("api.mustache", ".java");
apiTemplateFiles.put("apiService.mustache", ".java");
apiTemplateFiles.put("apiServiceImpl.mustache", ".java");
apiTemplateFiles.put("apiServiceFactory.mustache", ".java");
templateDir = "JavaJaxRS";
apiPackage = "io.swagger.api";
modelPackage = "io.swagger.model";
apiPackage = System.getProperty( "swagger.codegen.jaxrs.apipackage", "io.swagger.api") ;
modelPackage = System.getProperty( "swagger.codegen.jaxrs.modelpackage", "io.swagger.model" );
additionalProperties.put("invokerPackage", invokerPackage);
additionalProperties.put("groupId", groupId);
@ -146,4 +150,44 @@ public class JaxRSServerCodegen extends JavaClientCodegen implements CodegenConf
}
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;
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.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 {
protected String apiVersion = "1.0.0";
@ -156,8 +164,10 @@ public class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig
@Override
public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
Map<String, Object> objectMap = (Map<String, Object>)objs.get("operations");
List<CodegenOperation> operations = (List<CodegenOperation>)objectMap.get("operation");
@SuppressWarnings("unchecked")
Map<String, Object> objectMap = (Map<String, Object>) objs.get("operations");
@SuppressWarnings("unchecked")
List<CodegenOperation> operations = (List<CodegenOperation>) objectMap.get("operation");
for(CodegenOperation operation : operations) {
operation.httpMethod = operation.httpMethod.toLowerCase();
List<CodegenParameter> params = operation.allParams;
@ -170,20 +180,14 @@ public class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig
resp.code = "default";
}
}
if(operation.examples != null && operation.examples.size() > 0) {
List<Map<String, String>> examples = operation.examples;
for(int i = examples.size() - 1; i >= 0; i--) {
Map<String, String> example = examples.get(i);
String contentType = example.get("contentType");
if(contentType != null && contentType.indexOf("application/json") == 0) {
String jsonExample = example.get("example");
if(jsonExample != null) {
jsonExample = jsonExample.replaceAll("\\\\n", "\n");
example.put("example", jsonExample);
}
if(operation.examples != null && !operation.examples.isEmpty()) {
// Leave application/json* items only
for (Iterator<Map<String, String>> it = operation.examples.iterator(); it.hasNext();) {
final Map<String, String> example = it.next();
final String contentType = example.get("contentType");
if (contentType == null || !contentType.startsWith("application/json")) {
it.remove();
}
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("SWGFile.h", sourceFolder, "SWGFile.h"));
supportingFiles.add(new SupportingFile("SWGFile.m", sourceFolder, "SWGFile.m"));
supportingFiles.add(new SupportingFile("SWGDate.h", sourceFolder, "SWGDate.h"));
supportingFiles.add(new SupportingFile("SWGDate.m", sourceFolder, "SWGDate.m"));
supportingFiles.add(new SupportingFile("JSONValueTransformer+ISO8601.m", sourceFolder, "JSONValueTransformer+ISO8601.m"));
supportingFiles.add(new SupportingFile("JSONValueTransformer+ISO8601.h", sourceFolder, "JSONValueTransformer+ISO8601.h"));
supportingFiles.add(new SupportingFile("Podfile.mustache", "", "Podfile"));
}

View File

@ -84,6 +84,7 @@ public class PhpClientCodegen extends DefaultCodegen implements CodegenConfig {
typeMapping.put("list", "array");
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("APIClientException.mustache", packagePath + "/lib", "APIClientException.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");
templateDir = "python";
apiPackage = invokerPackage;
apiPackage = invokerPackage + ".apis";
modelPackage = invokerPackage + ".models";
languageSpecificPrimitives.clear();
languageSpecificPrimitives.add("int");
languageSpecificPrimitives.add("float");
languageSpecificPrimitives.add("long");
languageSpecificPrimitives.add("list");
languageSpecificPrimitives.add("bool");
languageSpecificPrimitives.add("str");
@ -49,7 +48,7 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
typeMapping.clear();
typeMapping.put("integer", "int");
typeMapping.put("float", "float");
typeMapping.put("long", "long");
typeMapping.put("long", "int");
typeMapping.put("double", "float");
typeMapping.put("array", "list");
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("setup.mustache", eggPackage, "setup.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__model.mustache", modelPackage.replace('.', File.separatorChar), "__init__.py"));
supportingFiles.add(new SupportingFile("__init__api.mustache", apiPackage.replace('.', File.separatorChar), "__init__.py"));
}
@Override
@ -113,9 +115,9 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
if(languageSpecificPrimitives.contains(type)) {
return type;
}
} else {
type = toModelName(swaggerType);
}
else
type = swaggerType;
return type;
}
@ -133,9 +135,9 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
if (name.matches("^[A-Z_]*$"))
name = name.toLowerCase();
// camelize (lower first character) the variable name
// underscore the variable name
// petId => pet_id
name = underscore(name);
name = underscore(dropDots(name));
// for reserved word or word starting with number, append _
if(reservedWords.contains(name) || name.matches("^\\d.*"))
@ -144,6 +146,10 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
return name;
}
private static String dropDots(String str) {
return str.replaceAll("\\.", "_");
}
@Override
public String toParamName(String 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");
// underscore the model file name
// PhoneNumber.rb => phone_number.rb
return underscore(name);
// PhoneNumber => phone_number
return underscore(dropDots(name));
}
@Override

View File

@ -8,10 +8,9 @@ import java.util.*;
import java.io.File;
public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
protected String invokerPackage = "com.wordnik.client";
protected String groupId = "com.wordnik";
protected String artifactId = "swagger-client";
protected String artifactVersion = "1.0.0";
protected String gemName = "swagger_client";
protected String moduleName = null;
protected String libFolder = "lib";
public CodegenType getTag() {
return CodegenType.CLIENT;
@ -25,10 +24,18 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
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() {
super();
modelPackage = "models";
apiPackage = "lib";
moduleName = generateModuleName();
modelPackage = gemName + "/models";
apiPackage = gemName + "/api";
outputFolder = "generated-code/ruby";
modelTemplateFiles.put("model.mustache", ".rb");
apiTemplateFiles.put("api.mustache", ".rb");
@ -39,17 +46,15 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
reservedWords = new HashSet<String> (
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",
"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",
"if", "not", "return", "undef", "yield")
);
additionalProperties.put("invokerPackage", invokerPackage);
additionalProperties.put("groupId", groupId);
additionalProperties.put("artifactId", artifactId);
additionalProperties.put("artifactVersion", artifactVersion);
additionalProperties.put("gemName", gemName);
additionalProperties.put("moduleName", moduleName);
languageSpecificPrimitives.add("int");
languageSpecificPrimitives.add("array");
@ -64,15 +69,18 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
typeMapping.put("List", "array");
typeMapping.put("map", "map");
supportingFiles.add(new SupportingFile("swagger-client.gemspec.mustache", "", "swagger-client.gemspec"));
supportingFiles.add(new SupportingFile("swagger-client.mustache", "", "lib/swagger-client.rb"));
supportingFiles.add(new SupportingFile("swagger.mustache", "", "lib/swagger.rb"));
supportingFiles.add(new SupportingFile("monkey.mustache", "", "lib/monkey.rb"));
supportingFiles.add(new SupportingFile("swagger/request.mustache", "", "lib/swagger/request.rb"));
supportingFiles.add(new SupportingFile("swagger/response.mustache", "", "lib/swagger/response.rb"));
supportingFiles.add(new SupportingFile("swagger/version.mustache", "", "lib/swagger/version.rb"));
supportingFiles.add(new SupportingFile("swagger/configuration.mustache", "", "lib/swagger/configuration.rb"));
supportingFiles.add(new SupportingFile("base_object.mustache", "", "models/base_object.rb"));
String baseFolder = "lib/" + gemName;
String swaggerFolder = baseFolder + "/swagger";
String modelFolder = baseFolder + "/models";
supportingFiles.add(new SupportingFile("swagger_client.gemspec.mustache", "", gemName + ".gemspec"));
supportingFiles.add(new SupportingFile("swagger_client.mustache", "lib", gemName + ".rb"));
supportingFiles.add(new SupportingFile("monkey.mustache", baseFolder, "monkey.rb"));
supportingFiles.add(new SupportingFile("swagger.mustache", baseFolder, "swagger.rb"));
supportingFiles.add(new SupportingFile("swagger/request.mustache", swaggerFolder, "request.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
@ -82,11 +90,11 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
@Override
public String apiFileFolder() {
return outputFolder + "/" + apiPackage().replace('.', File.separatorChar);
return outputFolder + File.separatorChar + "lib" + File.separatorChar + gemName + File.separatorChar + "api";
}
public String modelFileFolder() {
return outputFolder + "/" + modelPackage().replace('.', File.separatorChar);
return outputFolder + File.separatorChar + "lib" + File.separatorChar + gemName + File.separatorChar + "models";
}
@Override
@ -150,13 +158,13 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
// should be the same as variable name
return toVarName(name);
}
@Override
public String toModelName(String name) {
// model name cannot use reserved keyword, e.g. return
if(reservedWords.contains(name))
throw new RuntimeException(name + " (reserved word) cannot be used as a model name");
// camelize the model name
// phone_number => PhoneNumber
return camelize(name);
@ -167,11 +175,11 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
// model name cannot use reserved keyword, e.g. return
if(reservedWords.contains(name))
throw new RuntimeException(name + " (reserved word) cannot be used as a model name");
// underscore the model file name
// PhoneNumber.rb => phone_number.rb
return underscore(name);
}
}
@Override
public String toApiFilename(String name) {
@ -186,7 +194,7 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
public String toApiName(String name) {
if(name.length() == 0)
return "DefaultApi";
// e.g. phone_number_api => PhoneNumberApi
// e.g. phone_number_api => PhoneNumberApi
return camelize(name) + "Api";
}
@ -196,8 +204,17 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
if(reservedWords.contains(operationId))
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}};
import {{modelPackage}}.*;
import {{package}}.{{classname}}Service;
import {{package}}.factories.{{classname}}ServiceFactory;
import com.wordnik.swagger.annotations.ApiParam;
@ -21,26 +23,31 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.*;
@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")
{{#operations}}
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 class {{classname}} {
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}})
throws NotFoundException {
// do some magic!
return Response.ok().entity(new ApiResponseMessage(ApiResponseMessage.OK, "magic!")).build();
}
{{/operation}}
throws NotFoundException {
// do some magic!
return delegate.{{nickname}}({{#allParams}}{{#isFile}}fileDetail{{/isFile}}{{^isFile}}{{paramName}}{{/isFile}}{{#hasMore}},{{/hasMore}}{{/allParams}});
}
{{/operation}}
}
{{/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>
</executions>
</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>
</build>
<dependencies>
@ -129,7 +148,7 @@
</repository>
</repositories>
<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>
<jersey-version>1.13</jersey-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}}
@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.SwaggerYamlGenerator
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 {
repositories {
jcenter()
}
dependencies {
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'
{{#useAndroidMavenGradlePlugin}}
apply plugin: 'com.github.dcendents.android-maven'
{{/useAndroidMavenGradlePlugin}}
android {
compileSdkVersion 22
@ -22,6 +34,17 @@ android {
minSdkVersion 14
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 "org.apache.httpcomponents:httpcore:$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"
}
@ -48,7 +76,18 @@ afterEvaluate {
task.dependsOn variant.javaCompile
task.from variant.javaCompile.destinationDir
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);
}
}
}
{{#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.Collections.Generic;
using RestSharp;
using {{invokerPackage}};
using {{modelPackage}};
{{#imports}}
@ -9,101 +10,73 @@ namespace {{package}} {
{{#operations}}
public class {{classname}} {
string basePath;
private readonly ApiInvoker apiInvoker = ApiInvoker.GetInstance();
protected RestClient restClient;
public {{classname}}(String basePath = "{{basePath}}")
{
this.basePath = basePath;
this.restClient = new RestClient(basePath);
}
public ApiInvoker getInvoker() {
return apiInvoker;
}
// Sets the endpoint base url for the services being accessed
public void setBasePath(string basePath) {
/// <summary>
/// Sets the endpoint base url for the services being accessed
/// </summary>
/// <param name="basePath"> Base URL
/// <returns></returns>
public void SetBasePath(string basePath) {
this.basePath = basePath;
}
// Gets the endpoint base url for the services being accessed
public String getBasePath() {
return basePath;
/// <summary>
/// Gets the endpoint base url for the services being accessed
/// <returns>Base URL</returns>
/// </summary>
public String GetBasePath() {
return this.basePath;
}
{{#operation}}
/// <summary>
/// {{summary}} {{notes}}
/// </summary>
{{#allParams}}/// <param name="{{paramName}}">{{description}}</param>
{{#hasMore}} {{/hasMore}}{{/allParams}}
/// <returns></returns>
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}};
{{#allParams}} /// <param name="{{paramName}}">{{description}}</param>
{{/allParams}}
/// <returns>{{#returnType}}{{{returnType}}}{{/returnType}}</returns>
public {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{nickname}} ({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) {
// query params
var queryParams = new Dictionary<String, String>();
var headerParams = new Dictionary<String, String>();
var formParams = new Dictionary<String, object>();
var _request = new RestRequest("{{path}}", Method.{{httpMethod}});
{{#requiredParamCount}}
// verify required params are set
if ({{/requiredParamCount}}{{#requiredParams}} {{paramName}} == null {{#hasMore}}|| {{/hasMore}}{{/requiredParams}}{{#requiredParamCount}}) {
throw new ApiException(400, "missing required params");
{{#allParams}}{{#required}}
// verify the required parameter '{{paramName}}' is set
if ({{paramName}} == null) throw new ApiException(400, "Missing required parameter '{{paramName}}' when calling {{nickname}}");
{{/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){
queryParams.Add("{{baseName}}", apiInvoker.ParameterToString({{paramName}}));
}
_request.AddUrlSegment("format", "json"); // set format to json by default
{{#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}}
{{#headerParams}}headerParams.Add("{{baseName}}", apiInvoker.ParameterToString({{paramName}}));
{{#headerParams}} if ({{paramName}} != null) _request.AddHeader("{{baseName}}", ApiInvoker.ParameterToString({{paramName}})); // header parameter
{{/headerParams}}
{{#formParams}}if ({{paramName}} != null){
if({{paramName}} is byte[]) {
formParams.Add("{{baseName}}", {{paramName}});
} else {
formParams.Add("{{baseName}}", apiInvoker.ParameterToString({{paramName}}));
}
}
{{#formParams}}if ({{paramName}} != null) {{#isFile}}_request.AddFile("{{baseName}}", {{paramName}});{{/isFile}}{{^isFile}}_request.AddParameter("{{baseName}}", ApiInvoker.ParameterToString({{paramName}})); // form parameter{{/isFile}}
{{/formParams}}
{{#bodyParam}}_request.AddParameter("application/json", ApiInvoker.Serialize({{paramName}}), ParameterType.RequestBody); // http body (model) parameter
{{/bodyParam}}
try {
if (typeof({{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}) == typeof(byte[])) {
{{#returnType}}
var response = apiInvoker.invokeBinaryAPI(basePath, path, "GET", queryParams, null, headerParams, formParams);
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;
}
// make the HTTP request
IRestResponse response = restClient.Execute(_request);
if (((int)response.StatusCode) >= 400) {
throw new ApiException ((int)response.StatusCode, "Error calling {{nickname}}: " + response.Content);
}
{{#returnType}}return ({{{returnType}}}) ApiInvoker.Deserialize(response.Content, typeof({{{returnType}}}));{{/returnType}}{{^returnType}}
return;{{/returnType}}
}
{{/operation}}
}

View File

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

View File

@ -1,235 +1,81 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Newtonsoft.Json;
namespace {{invokerPackage}} {
public class ApiInvoker {
private static readonly ApiInvoker _instance = new ApiInvoker();
private Dictionary<String, String> defaultHeaderMap = new Dictionary<String, String>();
namespace {{invokerPackage}} {
public class ApiInvoker {
private static Dictionary<String, String> defaultHeaderMap = new Dictionary<String, String>();
public static ApiInvoker GetInstance() {
return _instance;
}
/// <summary>
/// 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>
/// Add default header
/// </summary>
/// <param name="key"> Header field name
/// <param name="value"> Header field value
/// <returns></returns>
public void addDefaultHeader(string key, string value) {
defaultHeaderMap.Add(key, value);
}
/// <summary>
/// Get default header
/// </summary>
/// <returns>Dictionary of default header</returns>
public static Dictionary<String, String> GetDefaultHeader() {
return defaultHeaderMap;
}
/// <summary>
/// escape string (url-encoded)
/// </summary>
/// <param name="str"> String to be escaped
/// <returns>Escaped string</returns>
public string escapeString(string str) {
return str;
}
/// <summary>
/// escape string (url-encoded)
/// </summary>
/// <param name="str"> String to be escaped
/// <returns>Escaped string</returns>
public static string EscapeString(string str) {
return str;
}
/// <summary>
/// if parameter is DateTime, output in ISO8601 format, otherwise just return the string
/// </summary>
/// <param name="obj"> The parameter (header, path, query, form)
/// <returns>Formatted string</returns>
public string ParameterToString(object obj)
/// <summary>
/// if parameter is DateTime, output in ISO8601 format, otherwise just return the string
/// </summary>
/// <param name="obj"> The parameter (header, path, query, form)
/// <returns>Formatted string</returns>
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);
}
/// <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 JsonConvert.DeserializeObject(json, type);
}
catch (IOException e) {
throw new ApiException(500, e.Message);
}
catch (IOException e) {
throw new ApiException(500, e.Message);
}
}
public static string serialize(object obj) {
try
{
return obj != null ? JsonConvert.SerializeObject(obj) : null;
}
catch (Exception e) {
throw new ApiException(500, e.Message);
}
}
public string invokeAPI(string host, string path, string method, Dictionary<String, String> queryParams, object body, Dictionary<String, String> headerParams, Dictionary<String, object> formParams)
/// <summary>
/// Serialize an object into JSON string
/// </summary>
/// <param name="obj"> Object
/// <returns>JSON string</returns>
public static string Serialize(object obj) {
try
{
return invokeAPIInternal(host, path, method, false, queryParams, body, headerParams, formParams) as string;
return obj != null ? JsonConvert.SerializeObject(obj) : null;
}
public byte[] invokeBinaryAPI(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, 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;
catch (Exception e) {
throw new ApiException(500, e.Message);
}
}
}
}

View File

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

View File

@ -35,7 +35,7 @@
{{#examples}}
<h3 class="field-label">Example data</h3>
<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}}
</div> <!-- method -->
<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'
pod 'AFNetworking', '~> 2.1'
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 (^)(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];
// remove format in URL if needed

View File

@ -24,6 +24,8 @@ class APIClient {
public static $GET = "GET";
public static $PUT = "PUT";
public static $DELETE = "DELETE";
private static $default_header = array();
/*
* @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";
/**
* @param string $host the address of the API server
* @param string $headerName a header to pass on requests
* @param string $host Base url of the API server (optional)
*/
function __construct($host, $headerName = null, $headerValue = null) {
$this->host = $host;
$this->headerName = $headerName;
$this->headerValue = $headerValue;
function __construct($host = null) {
if ($host == null) {
$this->host = '{{basePath}}';
} 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) {
if (!is_string($user_agent)) {
throw new Exception('User-agent must be a string.');
}
if (!is_string($user_agent))
throw new \InvalidArgumentException('User-agent must be a string.');
$this->user_agent= $user_agent;
}
/**
* @param integer $seconds Number of seconds before timing out [set to 0 for no timeout]
*/
*/
public function setTimeout($seconds) {
if (!is_numeric($seconds)) {
throw new Exception('Timeout variable must be numeric.');
}
if (!is_numeric($seconds))
throw new \InvalidArgumentException('Timeout variable must be numeric.');
$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 $method method to call
@ -76,23 +151,22 @@ class APIClient {
* @return mixed
*/
public function callAPI($resourcePath, $method, $queryParams, $postData,
$headerParams) {
$headerParams, $authSettings) {
$headers = array();
# Allow API key from $headerParams to override default
$added_api_key = False;
# determine authentication setting
$this->updateParamsForAuth($headerParams, $queryParams, $authSettings);
# construct the http header
if ($headerParams != null) {
# add default header
$headerParams = array_merge((array)self::$default_header, $headerParams);
foreach ($headerParams as $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
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
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
if (! empty($queryParams)) {
@ -130,7 +205,7 @@ class APIClient {
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
} 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);
@ -261,7 +336,6 @@ class APIClient {
* @param string $class class name is passed as a string
* @return object an instance of $class
*/
public static function deserialize($data, $class)
{
if (null === $data) {
@ -304,5 +378,37 @@ class APIClient {
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}}
// verify the required parameter '{{paramName}}' is set
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}}
@ -54,12 +54,11 @@ class {{classname}} {
$queryParams = array();
$headerParams = array();
$formParams = array();
$_header_accept = '{{#produces}}{{mediaType}}{{#hasMore}}, {{/hasMore}}{{/produces}}';
if ($_header_accept !== '') {
$_header_accept = $this->apiClient->selectHeaderAccept(array({{#produces}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/produces}}));
if (!is_null($_header_accept)) {
$headerParams['Accept'] = $_header_accept;
}
$_header_content_type = array({{#consumes}}'{{mediaType}}'{{#hasMore}},{{/hasMore}}{{/consumes}});
$headerParams['Content-Type'] = count($_header_content_type) > 0 ? $_header_content_type[0] : 'application/json';
$headerParams['Content-Type'] = $this->apiClient->selectHeaderContentType(array({{#consumes}}'{{mediaType}}'{{#hasMore}},{{/hasMore}}{{/consumes}}));
{{#queryParams}}// query params
if(${{paramName}} !== null) {
@ -92,18 +91,20 @@ class {{classname}} {
$httpBody = $formParams;
}
// authentication setting, if any
$authSettings = array({{#authMethods}}'{{name}}'{{#hasMore}}, {{/hasMore}}{{/authMethods}});
// make the API Call
$response = $this->apiClient->callAPI($resourcePath, $method,
$queryParams, $httpBody,
$headerParams);
$headerParams, $authSettings);
{{#returnType}}if(! $response) {
return null;
}
$responseObject = $this->apiClient->deserialize($response,
'{{returnType}}');
return $responseObject;{{/returnType}}
$responseObject = $this->apiClient->deserialize($response,'{{returnType}}');
return $responseObject;{{/returnType}}
}
{{/operation}}
{{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
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
"""Add all of the modules in the current directory to __all__"""
import os
from __future__ import absolute_import
{{#models}}{{#model}}
from .{{classVarName}} import {{classname}}
# import models into model package
{{#models}}{{#model}}from .{{classVarName}} import {{classname}}
{{/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
"""Add all of the modules in the current directory to __all__"""
import os
from __future__ import absolute_import
# import models into package
{{#models}}{{#model}}
from .models.{{classVarName}} import {{classname}}
# import models into sdk package
{{#models}}{{#model}}from .models.{{classVarName}} import {{classname}}
{{/model}}{{/models}}
# import apis into package
{{#apiInfo}}{{#apis}}
from .{{classVarName}} import {{classname}}
# import apis into sdk package
{{#apiInfo}}{{#apis}}from .apis.{{classVarName}} import {{classname}}
{{/apis}}{{/apiInfo}}
# 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.
"""
from __future__ import absolute_import
import sys
import os
import urllib
from models import *
# python 2 and python 3 compatibility library
from six import iteritems
from ..util import remove_none
{{#operations}}
class {{classname}}(object):
def __init__(self, apiClient):
self.apiClient = apiClient
{{newline}}
def __init__(self, api_client):
self.api_client = api_client
{{#operation}}
def {{nickname}}(self, {{#requiredParams}}{{paramName}}{{#defaultValue}} = None{{/defaultValue}}, {{/requiredParams}}**kwargs):
"""{{{summary}}}
def {{nickname}}(self, {{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}**kwargs):
"""
{{{summary}}}
{{{notes}}}
Args:
{{#allParams}}{{paramName}}, {{dataType}}: {{{description}}} {{^optional}}(required){{/optional}}{{#optional}}(optional){{/optional}}
{{/allParams}}
Returns: {{returnType}}
{{#allParams}}:param {{dataType}} {{paramName}}: {{{description}}} {{#required}}(required){{/required}}{{#optional}}(optional){{/optional}}
{{/allParams}}
:return: {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}None{{/returnType}}
"""
allParams = [{{#allParams}}'{{paramName}}'{{#hasMore}}, {{/hasMore}}{{/allParams}}]
{{#allParams}}{{#required}}
# 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()
for (key, val) in params['kwargs'].iteritems():
if key not in allParams:
for key, val in iteritems(params['kwargs']):
if key not in all_params:
raise TypeError("Got an unexpected keyword argument '%s' to method {{nickname}}" % key)
params[key] = val
del params['kwargs']
resourcePath = '{{path}}'
resourcePath = resourcePath.replace('{format}', 'json')
resource_path = '{{path}}'.replace('{format}', 'json')
method = '{{httpMethod}}'
queryParams = {}
headerParams = {}
formParams = {}
files = {}
bodyParam = None
path_params = remove_none(dict({{#pathParams}}{{baseName}}=params.get('{{paramName}}'){{#hasMore}}, {{/hasMore}}{{/pathParams}}))
query_params = remove_none(dict({{#queryParams}}{{baseName}}=params.get('{{paramName}}'){{#hasMore}}, {{/hasMore}}{{/queryParams}}))
header_params = remove_none(dict({{#headerParams}}{{baseName}}=params.get('{{paramName}}'){{#hasMore}}, {{/hasMore}}{{/headerParams}}))
form_params = remove_none(dict({{#formParams}}{{^isFile}}{{baseName}}=params.get('{{paramName}}'){{#hasMore}}, {{/hasMore}}{{/isFile}}{{/formParams}}))
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}}]
headerParams['Accept'] = ', '.join(accepts)
header_params['Accept'] = ', '.join(accepts)
content_types = [{{#consumes}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/consumes}}]
headerParams['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)
header_params['Content-Type'] = content_types[0] if len(content_types) > 0 else 'application/json'
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}}
if not response:
return None
responseObject = self.apiClient.deserialize(response, '{{returnType}}')
return responseObject
{{/returnType}}
{{newline}}
{{newline}}
{{/operation}}
{{newline}}
return response
{{/returnType}}{{/operation}}
{{/operations}}
{{newline}}

View File

@ -20,33 +20,40 @@ Copyright 2015 Reverb Technologies, Inc.
{{#model}}
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):
"""
Attributes:
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.
Swagger model
: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 = {
{{#vars}}
'{{name}}': '{{{datatype}}}'{{#hasMore}},
{{/hasMore}}
{{/vars}}{{newline}}
self.swagger_types = {
{{#vars}}'{{name}}': '{{{datatype}}}'{{#hasMore}},
{{/hasMore}}{{/vars}}
}
self.attributeMap = {
{{#vars}}
'{{name}}': '{{baseName}}'{{#hasMore}},{{/hasMore}}
{{/vars}}
self.attribute_map = {
{{#vars}}'{{name}}': '{{baseName}}'{{#hasMore}},
{{/hasMore}}{{/vars}}
}
{{#vars}}
{{#description}}#{{description}}
{{/description}}
self.{{name}} = None # {{{datatype}}}
{{#description}}# {{description}}{{/description}}
self.{{name}} = None # {{{datatype}}}
{{/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}}
{{/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:
# http://pypi.python.org/pypi/setuptools
REQUIRES = []
REQUIRES = ["urllib3 >= 1.10", "six >= 1.9"]
setup(
name="{{module}}",
@ -30,3 +30,11 @@ setup(
)
{{/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
templates."""
import sys
from __future__ import absolute_import
from . import models
from .rest import RESTClient
import os
import re
import urllib
import urllib2
import httplib
import json
import datetime
import mimetypes
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):
"""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):
self.defaultHeaders = {}
if (headerName is not None):
self.defaultHeaders[headerName] = headerValue
Generic API client for Swagger client library builds
:param host: The base path for the server to call
: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.cookie = None
self.boundary = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(30))
# Set default User-Agent.
self.user_agent = 'Python-Swagger'
@property
def user_agent(self):
return self.defaultHeaders['User-Agent']
return self.default_headers['User-Agent']
@user_agent.setter
def user_agent(self, value):
self.defaultHeaders['User-Agent'] = value
self.default_headers['User-Agent'] = value
def setDefaultHeader(self, headerName, headerValue):
self.defaultHeaders[headerName] = headerValue
def set_default_header(self, header_name, header_value):
self.default_headers[header_name] = header_value
def callAPI(self, resourcePath, method, queryParams, postData,
headerParams=None, files=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)
def call_api(self, resource_path, method, path_params=None, query_params=None, header_params=None,
body=None, post_params=None, files=None, response=None):
# headers parameters
headers = self.default_headers.copy()
headers.update(header_params)
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:
# Need to remove None values, these should not be sent
sentQueryParams = {}
for param, value in queryParams.items():
if value is not None:
sentQueryParams[param] = ApiClient.sanitizeForSerialization(value)
url = url + '?' + urllib.urlencode(sentQueryParams)
# query parameters
if query_params:
query_params = ApiClient.sanitize_for_serialization(query_params)
query_params = {k: self.to_path_value(v) for k, v in iteritems(query_params)}
if method in ['GET']:
#Options to add statements later on and for compatibility
pass
# post parameters
if post_params:
post_params = self.prepare_post_parameters(post_params, files)
post_params = ApiClient.sanitize_for_serialization(post_params)
elif method in ['POST', 'PUT', 'DELETE']:
if postData:
postData = ApiClient.sanitizeForSerialization(postData)
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)
# body
if body:
body = ApiClient.sanitize_for_serialization(body)
# 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:
raise Exception('Method ' + method + ' is not recognized.')
return None
request = MethodRequest(method=method, url=url, headers=headers,
data=data)
def to_path_value(self, obj):
"""
Convert a string or object to a path-friendly value
:param obj: object or string value
# Make the request
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
:return string: quoted value
"""
if type(obj) == list:
return ','.join(obj)
@ -125,12 +116,12 @@ class ApiClient(object):
return str(obj)
@staticmethod
def sanitizeForSerialization(obj):
def sanitize_for_serialization(obj):
"""
Sanitize an object for Request.
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 list, santize each element in the list.
If obj is dict, return the dict.
@ -138,113 +129,80 @@ class ApiClient(object):
"""
if isinstance(obj, type(None)):
return None
elif isinstance(obj, (str, int, long, float, bool, file)):
elif isinstance(obj, (str, int, float, bool, tuple)):
return obj
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)):
return obj.isoformat()
else:
if isinstance(obj, dict):
objDict = obj
obj_dict = obj
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.
# Convert attribute name to json key in model definition for request.
objDict = {obj.attributeMap[key]: val
for key, val in obj.__dict__.iteritems()
if key != 'swaggerTypes' and key != 'attributeMap' and val is not None}
return {key: ApiClient.sanitizeForSerialization(val)
for (key, val) in objDict.iteritems()}
obj_dict = {obj.attribute_map[key]: val
for key, val in iteritems(obj.__dict__)
if key != 'swagger_types' and key != 'attribute_map' and val is not None}
return {key: ApiClient.sanitize_for_serialization(val)
for key, val in iteritems(obj_dict)}
def buildMultipartFormData(self, postData, files):
def escape_quotes(s):
return s.replace('"', '\\"')
def deserialize(self, obj, obj_class):
"""
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():
lines.extend((
'--{0}'.format(self.boundary),
'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
:return object: deserialized object
"""
# Have to accept obj_class as string or actual type. Type could be a
# native Python type, or one of the model classes.
if type(objClass) == str:
if 'list[' in objClass:
match = re.match('list\[(.*)\]', objClass)
subClass = match.group(1)
return [self.deserialize(subObj, subClass) for subObj in obj]
if type(obj_class) == str:
if 'list[' in obj_class:
match = re.match('list\[(.*)\]', obj_class)
sub_class = match.group(1)
return [self.deserialize(sub_obj, sub_class) for sub_obj in obj]
if (objClass in ['int', 'float', 'long', 'dict', 'list', 'str', 'bool', 'datetime']):
objClass = eval(objClass)
if obj_class in ['int', 'float', 'dict', 'list', 'str', 'bool', 'datetime']:
obj_class = eval(obj_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]:
return objClass(obj)
elif objClass == datetime:
if obj_class in [int, float, dict, list, str, bool]:
return obj_class(obj)
elif obj_class == datetime:
return self.__parse_string_to_datetime(obj)
instance = objClass()
instance = obj_class()
for attr, attrType in instance.swaggerTypes.iteritems():
if obj is not None and instance.attributeMap[attr] in obj and type(obj) in [list, dict]:
value = obj[instance.attributeMap[attr]]
if attrType in ['str', 'int', 'long', 'float', 'bool']:
attrType = eval(attrType)
for attr, attr_type in iteritems(instance.swagger_types):
if obj is not None and instance.attribute_map[attr] in obj and type(obj) in [list, dict]:
value = obj[instance.attribute_map[attr]]
if attr_type in ['str', 'int', 'float', 'bool']:
attr_type = eval(attr_type)
try:
value = attrType(value)
value = attr_type(value)
except UnicodeEncodeError:
value = unicode(value)
except TypeError:
value = value
setattr(instance, attr, value)
elif (attrType == 'datetime'):
elif attr_type == 'datetime':
setattr(instance, attr, self.__parse_string_to_datetime(value))
elif 'list[' in attrType:
match = re.match('list\[(.*)\]', attrType)
subClass = match.group(1)
subValues = []
elif 'list[' in attr_type:
match = re.match('list\[(.*)\]', attr_type)
sub_class = match.group(1)
sub_values = []
if not value:
setattr(instance, attr, None)
else:
for subValue in value:
subValues.append(self.deserialize(subValue, subClass))
setattr(instance, attr, subValues)
for sub_value in value:
sub_values.append(self.deserialize(sub_value, sub_class))
setattr(instance, attr, sub_values)
else:
setattr(instance, attr, self.deserialize(value, attrType))
setattr(instance, attr, self.deserialize(value, attr_type))
return instance
@ -260,16 +218,42 @@ class ApiClient(object):
except ImportError:
return string
class MethodRequest(urllib2.Request):
def __init__(self, *args, **kwargs):
"""Construct a MethodRequest. Usage is the same as for
`urllib2.Request` except it also takes an optional `method`
keyword argument. If supplied, `method` will be used instead of
the default."""
def request(self, method, url, query_params=None, headers=None, post_params=None, body=None):
"""
Perform http request using RESTClient.
"""
if method == "GET":
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"
module {{moduleName}}
{{#operations}}
class {{classname}}
basePath = "{{basePath}}"
# apiInvoker = APIInvoker
class {{classname}}
basePath = "{{basePath}}"
# apiInvoker = APIInvoker
{{#operation}}
{{newline}}
# {{summary}}
# {{notes}}
{{#allParams}}{{#required}} # @param {{paramName}} {{description}}
{{/required}}{{/allParams}} # @param [Hash] opts the optional parameters
{{#allParams}}{{^required}} # @option opts [{{dataType}}] :{{paramName}} {{description}}
{{/required}}{{/allParams}} # @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}
def self.{{nickname}}({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}opts = {})
{{#allParams}}{{#required}}
# verify the required parameter '{{paramName}}' is set
raise "Missing the required parameter '{{paramName}}' when calling {{nickname}}" if {{{paramName}}}.nil?
{{/required}}{{/allParams}}
# {{summary}}
# {{notes}}
{{#allParams}}{{#required}} # @param {{paramName}} {{description}}
{{/required}}{{/allParams}} # @param [Hash] opts the optional parameters
{{#allParams}}{{^required}} # @option opts [{{dataType}}] :{{paramName}} {{description}}
{{/required}}{{/allParams}} # @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}
def self.{{nickname}}({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}opts = {})
{{#allParams}}{{#required}}
# verify the required parameter '{{paramName}}' is set
raise "Missing the required parameter '{{paramName}}' when calling {{nickname}}" if {{{paramName}}}.nil?
{{/required}}{{/allParams}}
# resource path
path = "{{path}}".sub('{format}','json'){{#pathParams}}.sub('{' + '{{baseName}}' + '}', {{paramName}}.to_s){{/pathParams}}
# resource path
path = "{{path}}".sub('{format}','json'){{#pathParams}}.sub('{' + '{{baseName}}' + '}', {{paramName}}.to_s){{/pathParams}}
# query parameters
query_params = {}{{#queryParams}}{{#required}}
query_params[:'{{{baseName}}}'] = {{{paramName}}}{{/required}}{{/queryParams}}{{#queryParams}}{{^required}}
query_params[:'{{{baseName}}}'] = opts[:'{{{paramName}}}'] if opts[:'{{{paramName}}}']{{/required}}{{/queryParams}}
# query parameters
query_params = {}{{#queryParams}}{{#required}}
query_params[:'{{{baseName}}}'] = {{{paramName}}}{{/required}}{{/queryParams}}{{#queryParams}}{{^required}}
query_params[:'{{{baseName}}}'] = opts[:'{{{paramName}}}'] if opts[:'{{{paramName}}}']{{/required}}{{/queryParams}}
# header parameters
header_params = {}
# header parameters
header_params = {}
# HTTP header 'Accept' (if needed)
_header_accept = [{{#produces}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/produces}}]
_header_accept_result = Swagger::Request.select_header_accept(_header_accept) and header_params['Accept'] = _header_accept_result
# HTTP header 'Accept' (if needed)
_header_accept = [{{#produces}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/produces}}]
_header_accept_result = Swagger::Request.select_header_accept(_header_accept) and header_params['Accept'] = _header_accept_result
# HTTP header 'Content-Type'
_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[:'{{{baseName}}}'] = {{{paramName}}}{{/required}}{{/headerParams}}{{#headerParams}}{{^required}}
header_params[:'{{{baseName}}}'] = opts[:'{{{paramName}}}'] if opts[:'{{{paramName}}}']{{/required}}{{/headerParams}}
# HTTP header 'Content-Type'
_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[:'{{{baseName}}}'] = {{{paramName}}}{{/required}}{{/headerParams}}{{#headerParams}}{{^required}}
header_params[:'{{{baseName}}}'] = opts[:'{{{paramName}}}'] if opts[:'{{{paramName}}}']{{/required}}{{/headerParams}}
# form parameters
form_params = {}{{#formParams}}{{#required}}
form_params["{{baseName}}"] = {{paramName}}{{/required}}{{/formParams}}{{#formParams}}{{^required}}
form_params["{{baseName}}"] = opts[:'{{paramName}}'] if opts[:'{{paramName}}']{{/required}}{{/formParams}}
# form parameters
form_params = {}{{#formParams}}{{#required}}
form_params["{{baseName}}"] = {{paramName}}{{/required}}{{/formParams}}{{#formParams}}{{^required}}
form_params["{{baseName}}"] = opts[:'{{paramName}}'] if opts[:'{{paramName}}']{{/required}}{{/formParams}}
# http body (model)
{{^bodyParam}}post_body = nil
{{/bodyParam}}{{#bodyParam}}post_body = Swagger::Request.object_to_http_body({{#required}}{{{paramName}}}{{/required}}{{^required}}opts[:'{{{paramName}}}']{{/required}})
{{/bodyParam}}
# http body (model)
{{^bodyParam}}post_body = nil
{{/bodyParam}}{{#bodyParam}}post_body = Swagger::Request.object_to_http_body({{#required}}{{{paramName}}}{{/required}}{{^required}}opts[:'{{{paramName}}}']{{/required}})
{{/bodyParam}}
{{#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}}
{{#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}}
end
{{/operation}}
end
end
{{/operations}}
end

View File

@ -1,83 +1,83 @@
# base class containing fundamental method such as to_hash, build_from_hash and more
class BaseObject
module {{moduleName}}
# base class containing fundamental method such as to_hash, build_from_hash and more
class BaseObject
# return the object in the form of hash
def to_body
body = {}
self.class.attribute_map.each_pair do |key, value|
body[value] = self.send(key) unless self.send(key).nil?
# return the object in the form of hash
def to_body
body = {}
self.class.attribute_map.each_pair do |key, value|
body[value] = self.send(key) unless self.send(key).nil?
end
body
end
body
end
# build the object from hash
def build_from_hash(attributes)
return nil unless attributes.is_a?(Hash)
self.class.swagger_types.each_pair do |key, type|
if type =~ /^array\[(.*)\]/i
if attributes[self.class.attribute_map[key]].is_a?(Array)
self.send("#{key}=", attributes[self.class.attribute_map[key]].map{ |v| _deserialize($1, v) } )
# build the object from hash
def build_from_hash(attributes)
return nil unless attributes.is_a?(Hash)
self.class.swagger_types.each_pair do |key, type|
if type =~ /^array\[(.*)\]/i
if attributes[self.class.attribute_map[key]].is_a?(Array)
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
#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
# 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
# data not found in attributes(hash), not an issue as the data can be optional
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
self
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

View File

@ -1,41 +1,40 @@
require_relative 'base_object'
{{#models}}#{{description}}
{{#model}}class {{classname}} < BaseObject
attr_accessor {{#vars}}:{{{name}}}{{#hasMore}}, {{/hasMore}}{{/vars}}{{newline}}
# attribute mapping from ruby-style variable name to JSON key
def self.attribute_map
{
{{#vars}}
# {{description}}
:'{{{name}}}' => :'{{{baseName}}}'{{#hasMore}},{{/hasMore}}
{{/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}}
module {{moduleName}}
{{#models}} # {{description}}
{{#model}} class {{classname}} < BaseObject
attr_accessor {{#vars}}:{{{name}}}{{#hasMore}}, {{/hasMore}}{{/vars}}{{newline}}
# attribute mapping from ruby-style variable name to JSON key
def self.attribute_map
{
{{#vars}}
# {{description}}
:'{{{name}}}' => :'{{{baseName}}}'{{#hasMore}},{{/hasMore}}
{{/vars}}
}
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}}
{{/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 'json'
module Swagger
@configuration = Configuration.new
module {{moduleName}}
module Swagger
class << self
attr_accessor :logger
class << self
attr_accessor :logger
# A Swagger configuration object. Must act like a hash and return sensible
# 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
# 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?
attr_accessor :resources
# Configure logger. Default to use Rails
self.logger ||= configuration.logger || (defined?(Rails) ? Rails.logger : Logger.new(STDOUT))
# 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?
# remove :// from scheme
configuration.scheme.sub!(/:\/\//, '')
# Configure logger. Default to use Rails
self.logger ||= configuration.logger || (defined?(Rails) ? Rails.logger : Logger.new(STDOUT))
# remove http(s):// and anything after a slash
configuration.host.sub!(/https?:\/\//, '')
configuration.host = configuration.host.split('/').first
# remove :// from scheme
configuration.scheme.sub!(/:\/\//, '')
# Add leading and trailing slashes to base_path
configuration.base_path = "/#{configuration.base_path}".gsub(/\/+/, '/')
configuration.base_path = "" if configuration.base_path == "/"
end
def authenticated?
Swagger.configuration.auth_token.present?
end
def de_authenticate
Swagger.configuration.auth_token = nil
end
def authenticate
return if Swagger.authenticated?
if Swagger.configuration.username.blank? || Swagger.configuration.password.blank?
raise ClientError, "Username and password are required to authenticate."
# remove http(s):// and anything after a slash
configuration.host.sub!(/https?:\/\//, '')
configuration.host = configuration.host.split('/').first
# Add leading and trailing slashes to base_path
configuration.base_path = "/#{configuration.base_path}".gsub(/\/+/, '/')
configuration.base_path = "" if configuration.base_path == "/"
end
def authenticated?
Swagger.configuration.auth_token.present?
end
def de_authenticate
Swagger.configuration.auth_token = nil
end
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
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
class ServerError < StandardError
end
class ServerError < StandardError
end
class ClientError < StandardError
class ClientError < StandardError
end
end

View File

@ -1,22 +1,19 @@
module Swagger
class Configuration
require 'swagger/version'
module {{moduleName}}
module Swagger
class Configuration
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
@format = 'json'
@scheme = '{{scheme}}'
@host = '{{host}}'
@base_path = '{{contextPath}}'
@user_agent = "ruby-swagger-#{Swagger::VERSION}"
@inject_format = false
@force_ending_format = false
@camelize_params = true
# Defaults go in here..
def initialize
@format = 'json'
@scheme = '{{scheme}}'
@host = '{{host}}'
@base_path = '{{contextPath}}'
@user_agent = "ruby-swagger-#{Swagger::VERSION}"
@inject_format = false
@force_ending_format = false
@camelize_params = true
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
require 'uri'
require 'addressable/uri'
require 'typhoeus'
require "swagger/version"
attr_accessor :host, :path, :format, :params, :body, :http_method, :headers, :form_params
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
# Optionals parameters are :params, :headers, :body, :format, :host
#
def initialize(http_method, path, attributes={})
attributes[:format] ||= Swagger.configuration.format
attributes[:params] ||= {}
# api_key from headers hash trumps the default, even if its value is blank
if attributes[:headers].present? && attributes[:headers].has_key?(:api_key)
default_headers.delete(:api_key)
end
# Set default headers
default_headers = {
'Content-Type' => "application/#{attributes[:format].downcase}",
:api_key => Swagger.configuration.api_key,
'User-Agent' => Swagger.configuration.user_agent
}
# 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
# api_key from headers hash trumps the default, even if its value is blank
if attributes[:headers].present? && attributes[:headers].has_key?(:api_key)
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
# Merge argument headers into defaults
attributes[:headers] = default_headers.merge(attributes[:headers] || {})
# Construct a base URL
#
def url(options = {})
u = Addressable::URI.new(
: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
# Stick in the auth token if there is one
if Swagger.authenticated?
attributes[:headers].merge!({:auth_token => Swagger.configuration.auth_token})
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}")
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
# 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}"
# Construct a base URL
def url(options = {})
u = Addressable::URI.new(
: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
# 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
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
# Construct a query string from the query-string-type params
def query_string
# Iterate over all params,
# .. removing the ones that are part of the path itself.
# .. stringifying values so Addressable doesn't blow up.
query_values = {}
self.params.each_pair do |key, value|
next if self.path.include? "{#{key}}" # skip path params
next if value.blank? && value.class != FalseClass # skip empties
if Swagger.configuration.camelize_params
key = key.to_s.camelize(:lower).to_sym unless key.to_sym == :api_key # api_key is not a camelCased param
end
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
@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
# 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
data
else # http body is JSON
@body.is_a?(String) ? @body : @body.to_json
end
end
# Construct a query string from the query-string-type params
def query_string
# Iterate over all params,
# .. removing the ones that are part of the path itself.
# .. stringifying values so Addressable doesn't blow up.
query_values = {}
self.params.each_pair do |key, value|
next if self.path.include? "{#{key}}" # skip path params
next if value.blank? && value.class != FalseClass # skip empties
if Swagger.configuration.camelize_params
key = key.to_s.camelize(:lower).to_sym unless key.to_sym == :api_key # api_key is not a camelCased param
# 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
query_values[key] = value.to_s
_body.to_json
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,
)
# 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
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
# 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

View File

@ -1,70 +1,70 @@
module Swagger
module {{moduleName}}
module Swagger
class Response
require 'json'
class Response
require 'json'
attr_accessor :raw
attr_accessor :raw
def initialize(raw)
self.raw = raw
def initialize(raw)
self.raw = raw
case self.code
when 500..510 then raise(ServerError, self.error_message)
when 299..426 then raise(ClientError, self.error_message)
end
end
case self.code
when 500..510 then raise(ServerError, self.error_message)
when 299..426 then raise(ClientError, self.error_message)
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
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

View File

@ -1,4 +1,5 @@
module Swagger
VERSION = "4.06.08"
module {{moduleName}}
module Swagger
VERSION = "{{appVersion}}"
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",
"schema": {
"$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": {

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.models.properties.Property
import io.swagger.parser._
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.FlatSpec
import org.scalatest.Matchers
import scala.collection.JavaConverters._
import org.scalatest.{FlatSpec, Matchers}
@RunWith(classOf[JUnitRunner])
class CodegenTest extends FlatSpec with Matchers {
@ -95,6 +89,32 @@ class CodegenTest extends FlatSpec with Matchers {
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 {
val model = new SwaggerParser()
.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>
<relativePath>../..</relativePath>
</parent>
<groupId>com.wordnik</groupId>
<artifactId>swagger-generator</artifactId>
<packaging>war</packaging>
<name>swagger-generator</name>
<version>2.1.1-M2-SNAPSHOT</version>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<resources>

View File

@ -450,10 +450,10 @@
</repository>
</repositories>
<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>
<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>
<commons-io-version>2.3</commons-io-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