Merge pull request #2396 from demonfiddler/master

[JavaScript] Generator options and template improvements
This commit is contained in:
wing328 2016-03-18 16:11:35 +08:00
commit d839875d90
13 changed files with 1648 additions and 302 deletions

View File

@ -6,20 +6,35 @@ import java.util.*;
public class CodegenModel {
public String parent, parentSchema;
public List<String> interfaces;
// References to parent and interface CodegenModels. Only set when code generator supports inheritance.
public CodegenModel parentModel;
public List<CodegenModel> interfaceModels;
public String name, classname, description, classVarName, modelJson, dataType;
public String classFilename; // store the class file name, mainly used for import
public String unescapedDescription;
public String discriminator;
public String defaultValue;
public List<CodegenProperty> vars = new ArrayList<CodegenProperty>();
public List<CodegenProperty> allVars;
public List<String> allowableValues;
// list of all required parameters
public Set<String> mandatory = new HashSet<String>();
// Sorted sets of required parameters.
public Set<String> mandatory = new TreeSet<String>();
public Set<String> allMandatory;
public Set<String> imports = new TreeSet<String>();
public Boolean hasVars, emptyVars, hasMoreModels, hasEnums, isEnum;
public ExternalDocs externalDocs;
public Map<String, Object> vendorExtensions;
{
// By default these are the same collections. Where the code generator supports inheritance, composed models
// store the complete closure of owned and inherited properties in allVars and allMandatory.
allVars = vars;
allMandatory = mandatory;
}
}

View File

@ -45,9 +45,12 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -124,8 +127,36 @@ public class DefaultCodegen {
}
// override with any special post-processing for all models
@SuppressWarnings("static-method")
@SuppressWarnings({ "static-method", "unchecked" })
public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
if (supportsInheritance) {
// Index all CodegenModels by name.
Map<String, CodegenModel> allModels = new HashMap<String, CodegenModel>();
for (Entry<String, Object> entry : objs.entrySet()) {
String modelName = entry.getKey();
Map<String, Object> inner = (Map<String, Object>) entry.getValue();
List<Map<String, Object>> models = (List<Map<String, Object>>) inner.get("models");
for (Map<String, Object> mo : models) {
CodegenModel cm = (CodegenModel) mo.get("model");
allModels.put(modelName, cm);
}
}
// Fix up all parent and interface CodegenModel references.
for (CodegenModel cm : allModels.values()) {
if (cm.parent != null) {
cm.parentModel = allModels.get(cm.parent);
}
if (cm.interfaces != null && !cm.interfaces.isEmpty()) {
cm.interfaceModels = new ArrayList<CodegenModel>(cm.interfaces.size());
for (String intf : cm.interfaces) {
CodegenModel intfModel = allModels.get(intf);
if (intfModel != null) {
cm.interfaceModels.add(intfModel);
}
}
}
}
}
return objs;
}
@ -945,8 +976,18 @@ public class DefaultCodegen {
// TODO
} else if (model instanceof ComposedModel) {
final ComposedModel composed = (ComposedModel) model;
Map<String, Property> properties = new HashMap<String, Property>();
Map<String, Property> properties = new LinkedHashMap<String, Property>();
List<String> required = new ArrayList<String>();
Map<String, Property> allProperties;
List<String> allRequired;
if (supportsInheritance) {
allProperties = new LinkedHashMap<String, Property>();
allRequired = new ArrayList<String>();
m.allVars = new ArrayList<CodegenProperty>();
} else {
allProperties = null;
allRequired = null;
}
// parent model
final RefModel parent = (RefModel) composed.getParent();
if (parent != null) {
@ -954,31 +995,29 @@ public class DefaultCodegen {
m.parentSchema = parentRef;
m.parent = toModelName(parent.getSimpleRef());
addImport(m, m.parent);
if (!supportsInheritance && allDefinitions != null) {
if (allDefinitions != null) {
final Model parentModel = allDefinitions.get(m.parentSchema);
if (parentModel instanceof ModelImpl) {
final ModelImpl _parent = (ModelImpl) parentModel;
if (_parent.getProperties() != null) {
properties.putAll(_parent.getProperties());
}
if (_parent.getRequired() != null) {
required.addAll(_parent.getRequired());
}
if (supportsInheritance) {
addProperties(allProperties, allRequired, parentModel, allDefinitions);
} else {
addProperties(properties, required, parentModel, allDefinitions);
}
}
}
// interfaces (intermediate models)
if (allDefinitions != null && composed.getInterfaces() != null) {
if (composed.getInterfaces() != null) {
if (m.interfaces == null)
m.interfaces = new ArrayList<String>();
for (RefModel _interface : composed.getInterfaces()) {
final String interfaceRef = toModelName(_interface.getSimpleRef());
final Model interfaceModel = allDefinitions.get(interfaceRef);
if (interfaceModel instanceof ModelImpl) {
final ModelImpl _interfaceModel = (ModelImpl) interfaceModel;
if (_interfaceModel.getProperties() != null) {
properties.putAll(_interfaceModel.getProperties());
}
if (_interfaceModel.getRequired() != null) {
required.addAll(_interfaceModel.getRequired());
m.interfaces.add(interfaceRef);
addImport(m, interfaceRef);
if (allDefinitions != null) {
final Model interfaceModel = allDefinitions.get(interfaceRef);
if (supportsInheritance) {
addProperties(allProperties, allRequired, interfaceModel, allDefinitions);
} else {
addProperties(properties, required, interfaceModel, allDefinitions);
}
}
}
@ -990,15 +1029,12 @@ public class DefaultCodegen {
child = allDefinitions.get(childRef);
}
if (child != null && child instanceof ModelImpl) {
final ModelImpl _child = (ModelImpl) child;
if (_child.getProperties() != null) {
properties.putAll(_child.getProperties());
}
if (_child.getRequired() != null) {
required.addAll(_child.getRequired());
addProperties(properties, required, child, allDefinitions);
if (supportsInheritance) {
addProperties(allProperties, allRequired, child, allDefinitions);
}
}
addVars(m, properties, required);
addVars(m, properties, required, allProperties, allRequired);
} else {
ModelImpl impl = (ModelImpl) model;
if(impl.getEnum() != null && impl.getEnum().size() > 0) {
@ -1014,7 +1050,7 @@ public class DefaultCodegen {
addVars(m, impl.getProperties(), impl.getRequired());
}
if(m.vars != null) {
if (m.vars != null) {
for(CodegenProperty prop : m.vars) {
postProcessModelProperty(m, prop);
}
@ -1022,6 +1058,28 @@ public class DefaultCodegen {
return m;
}
protected void addProperties(Map<String, Property> properties, List<String> required, Model model,
Map<String, Model> allDefinitions) {
if (model instanceof ModelImpl) {
ModelImpl mi = (ModelImpl) model;
if (mi.getProperties() != null) {
properties.putAll(mi.getProperties());
}
if (mi.getRequired() != null) {
required.addAll(mi.getRequired());
}
} else if (model instanceof RefModel) {
String interfaceRef = toModelName(((RefModel) model).getSimpleRef());
Model interfaceModel = allDefinitions.get(interfaceRef);
addProperties(properties, required, interfaceModel, allDefinitions);
} else if (model instanceof ComposedModel) {
for (Model component :((ComposedModel) model).getAllOf()) {
addProperties(properties, required, component, allDefinitions);
}
}
}
/**
* Camelize the method name of the getter and setter
*
@ -1263,6 +1321,7 @@ public class DefaultCodegen {
property.containerType = "map";
MapProperty ap = (MapProperty) p;
CodegenProperty cp = fromProperty("inner", ap.getAdditionalProperties());
property.items = cp;
property.baseType = getSwaggerType(p);
if (!languageSpecificPrimitives.contains(cp.baseType)) {
@ -2167,45 +2226,65 @@ public class DefaultCodegen {
}
}
private void addVars(CodegenModel m, Map<String, Property> properties, Collection<String> required) {
if (properties != null && properties.size() > 0) {
private void addVars(CodegenModel m, Map<String, Property> properties, List<String> required) {
addVars(m, properties, required, null, null);
}
private void addVars(CodegenModel m, Map<String, Property> properties, List<String> required,
Map<String, Property> allProperties, List<String> allRequired) {
if (properties != null && !properties.isEmpty()) {
m.hasVars = true;
m.hasEnums = false;
final int totalCount = properties.size();
final Set<String> mandatory = required == null ? Collections.<String>emptySet() : new HashSet<String>(required);
int count = 0;
for (Map.Entry<String, Property> entry : properties.entrySet()) {
final String key = entry.getKey();
final Property prop = entry.getValue();
if (prop == null) {
LOGGER.warn("null property for " + key);
} else {
final CodegenProperty cp = fromProperty(key, prop);
cp.required = mandatory.contains(key) ? true : null;
if (cp.isEnum) {
m.hasEnums = true;
}
count += 1;
if (count != totalCount) {
cp.hasMore = true;
}
if (cp.isContainer != null) {
addImport(m, typeMapping.get("array"));
}
addImport(m, cp.baseType);
addImport(m, cp.complexType);
m.vars.add(cp);
}
}
m.mandatory = mandatory;
Set<String> mandatory = required == null ? Collections.<String> emptySet()
: new TreeSet<String>(required);
addVars(m, m.vars, properties, mandatory);
m.allMandatory = m.mandatory = mandatory;
} else {
m.emptyVars = true;
m.hasVars = false;
m.hasEnums = false;
}
if (allProperties != null) {
Set<String> allMandatory = allRequired == null ? Collections.<String> emptySet()
: new TreeSet<String>(allRequired);
addVars(m, m.allVars, allProperties, allMandatory);
m.allMandatory = allMandatory;
}
}
private void addVars(CodegenModel m, List<CodegenProperty> vars, Map<String, Property> properties, Set<String> mandatory) {
final int totalCount = properties.size();
int count = 0;
for (Map.Entry<String, Property> entry : properties.entrySet()) {
final String key = entry.getKey();
final Property prop = entry.getValue();
if (prop == null) {
LOGGER.warn("null property for " + key);
} else {
final CodegenProperty cp = fromProperty(key, prop);
cp.required = mandatory.contains(key) ? true : null;
if (cp.isEnum) {
// FIXME: if supporting inheritance, when called a second time for allProperties it is possible for
// m.hasEnums to be set incorrectly if allProperties has enumerations but properties does not.
m.hasEnums = true;
}
count++;
if (count != totalCount) {
cp.hasMore = true;
}
if (cp.isContainer != null) {
addImport(m, typeMapping.get("array"));
}
addImport(m, cp.baseType);
addImport(m, cp.complexType);
vars.add(cp);
}
}
}
/**

View File

@ -47,23 +47,27 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
@SuppressWarnings("hiding")
private static final Logger LOGGER = LoggerFactory.getLogger(JavascriptClientCodegen.class);
private static final String PROJECT_NAME = "projectName";
private static final String MODULE_NAME = "moduleName";
private static final String PROJECT_DESCRIPTION = "projectDescription";
private static final String PROJECT_VERSION = "projectVersion";
private static final String PROJECT_LICENSE_NAME = "projectLicenseName";
private static final String USE_PROMISES = "usePromises";
private static final String OMIT_MODEL_METHODS = "omitModelMethods";
public static final String PROJECT_NAME = "projectName";
public static final String MODULE_NAME = "moduleName";
public static final String PROJECT_DESCRIPTION = "projectDescription";
public static final String PROJECT_VERSION = "projectVersion";
public static final String PROJECT_LICENSE_NAME = "projectLicenseName";
public static final String USE_PROMISES = "usePromises";
public static final String USE_INHERITANCE = "useInheritance";
public static final String EMIT_MODEL_METHODS = "emitModelMethods";
public static final String EMIT_JS_DOC = "emitJSDoc";
protected String projectName;
protected String moduleName;
protected String projectDescription;
protected String projectVersion;
protected String projectLicenseName;
protected String sourceFolder = "src";
protected String localVariablePrefix = "";
protected boolean usePromises = false;
protected boolean omitModelMethods = false;
protected boolean usePromises;
protected boolean emitModelMethods;
protected boolean emitJSDoc = true;
protected String apiDocPath = "docs/";
protected String modelDocPath = "docs/";
@ -105,8 +109,36 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
);
defaultIncludes = new HashSet<String>(languageSpecificPrimitives);
instantiationTypes.put("array", "Array");
instantiationTypes.put("list", "Array");
instantiationTypes.put("map", "Object");
typeMapping.clear();
typeMapping.put("array", "Array");
typeMapping.put("map", "Object");
typeMapping.put("List", "Array");
typeMapping.put("boolean", "Boolean");
typeMapping.put("string", "String");
typeMapping.put("int", "Integer"); // Huh? What is JS Integer?
typeMapping.put("float", "Number");
typeMapping.put("number", "Number");
typeMapping.put("DateTime", "Date"); // Should this be dateTime?
typeMapping.put("Date", "Date"); // Should this be date?
typeMapping.put("long", "Integer");
typeMapping.put("short", "Integer");
typeMapping.put("char", "String");
typeMapping.put("double", "Number");
typeMapping.put("object", "Object");
typeMapping.put("integer", "Integer");
// binary not supported in JavaScript client right now, using String as a workaround
typeMapping.put("ByteArray", "String"); // I don't see ByteArray defined in the Swagger docs.
typeMapping.put("binary", "String");
importMapping.clear();
cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, CodegenConstants.SOURCE_FOLDER_DESC).defaultValue("src"));
cliOptions.add(new CliOption(CodegenConstants.LOCAL_VARIABLE_PREFIX, CodegenConstants.LOCAL_VARIABLE_PREFIX_DESC));
cliOptions.add(new CliOption(CodegenConstants.API_PACKAGE, CodegenConstants.API_PACKAGE_DESC));
cliOptions.add(new CliOption(CodegenConstants.MODEL_PACKAGE, CodegenConstants.MODEL_PACKAGE_DESC));
cliOptions.add(new CliOption(PROJECT_NAME,
"name of the project (Default: generated from info.title or \"swagger-js-client\")"));
cliOptions.add(new CliOption(MODULE_NAME,
@ -120,9 +152,15 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
cliOptions.add(new CliOption(USE_PROMISES,
"use Promises as return values from the client API, instead of superagent callbacks")
.defaultValue(Boolean.FALSE.toString()));
cliOptions.add(new CliOption(OMIT_MODEL_METHODS,
"omits generation of getters and setters for model classes")
cliOptions.add(new CliOption(EMIT_MODEL_METHODS,
"generate getters and setters for model properties")
.defaultValue(Boolean.FALSE.toString()));
cliOptions.add(new CliOption(EMIT_JS_DOC,
"generate JSDoc comments")
.defaultValue(Boolean.TRUE.toString()));
cliOptions.add(new CliOption(USE_INHERITANCE,
"use JavaScript prototype chains & delegation for inheritance")
.defaultValue(Boolean.TRUE.toString()));
}
@Override
@ -144,59 +182,47 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
public void processOpts() {
super.processOpts();
typeMapping = new HashMap<String, String>();
typeMapping.put("array", "Array");
typeMapping.put("List", "Array");
typeMapping.put("map", "Object");
typeMapping.put("object", "Object");
typeMapping.put("boolean", "Boolean");
typeMapping.put("char", "String");
typeMapping.put("string", "String");
typeMapping.put("short", "Integer");
typeMapping.put("int", "Integer");
typeMapping.put("integer", "Integer");
typeMapping.put("long", "Integer");
typeMapping.put("float", "Number");
typeMapping.put("double", "Number");
typeMapping.put("number", "Number");
typeMapping.put("DateTime", "Date");
typeMapping.put("Date", "Date");
typeMapping.put("file", "File");
// binary not supported in JavaScript client right now, using String as a workaround
typeMapping.put("binary", "String");
importMapping.clear();
if (additionalProperties.containsKey(PROJECT_NAME)) {
setProjectName(((String) additionalProperties.get(PROJECT_NAME)));
}
if (additionalProperties.containsKey(MODULE_NAME)) {
setModuleName(((String) additionalProperties.get(MODULE_NAME)));
}
if (additionalProperties.containsKey(PROJECT_DESCRIPTION)) {
setProjectDescription(((String) additionalProperties.get(PROJECT_DESCRIPTION)));
}
if (additionalProperties.containsKey(PROJECT_VERSION)) {
setProjectVersion(((String) additionalProperties.get(PROJECT_VERSION)));
}
if (additionalProperties.containsKey(PROJECT_LICENSE_NAME)) {
setProjectLicenseName(((String) additionalProperties.get(PROJECT_LICENSE_NAME)));
}
if (additionalProperties.containsKey(CodegenConstants.LOCAL_VARIABLE_PREFIX)) {
setLocalVariablePrefix((String) additionalProperties.get(CodegenConstants.LOCAL_VARIABLE_PREFIX));
}
if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) {
setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER));
}
if (additionalProperties.containsKey(USE_PROMISES)) {
setUsePromises(Boolean.parseBoolean((String)additionalProperties.get(USE_PROMISES)));
}
if (additionalProperties.containsKey(USE_INHERITANCE)) {
setUseInheritance(Boolean.parseBoolean((String)additionalProperties.get(USE_INHERITANCE)));
} else {
supportsInheritance = true;
}
if (additionalProperties.containsKey(EMIT_MODEL_METHODS)) {
setEmitModelMethods(Boolean.parseBoolean((String)additionalProperties.get(EMIT_MODEL_METHODS)));
}
if (additionalProperties.containsKey(EMIT_JS_DOC)) {
setEmitJSDoc(Boolean.parseBoolean((String)additionalProperties.get(EMIT_JS_DOC)));
}
}
@Override
public void preprocessSwagger(Swagger swagger) {
super.preprocessSwagger(swagger);
if (additionalProperties.containsKey(PROJECT_NAME)) {
projectName = ((String) additionalProperties.get(PROJECT_NAME));
}
if (additionalProperties.containsKey(MODULE_NAME)) {
moduleName = ((String) additionalProperties.get(MODULE_NAME));
}
if (additionalProperties.containsKey(PROJECT_DESCRIPTION)) {
projectDescription = ((String) additionalProperties.get(PROJECT_DESCRIPTION));
}
if (additionalProperties.containsKey(PROJECT_VERSION)) {
projectVersion = ((String) additionalProperties.get(PROJECT_VERSION));
}
if (additionalProperties.containsKey(CodegenConstants.LOCAL_VARIABLE_PREFIX)) {
localVariablePrefix = (String) additionalProperties.get(CodegenConstants.LOCAL_VARIABLE_PREFIX);
}
if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) {
sourceFolder = (String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER);
}
if (additionalProperties.containsKey(USE_PROMISES)) {
usePromises = Boolean.parseBoolean((String)additionalProperties.get(USE_PROMISES));
}
if (additionalProperties.containsKey(OMIT_MODEL_METHODS)) {
omitModelMethods = Boolean.parseBoolean((String)additionalProperties.get(OMIT_MODEL_METHODS));
}
if (swagger.getInfo() != null) {
Info info = swagger.getInfo();
if (StringUtils.isBlank(projectName) && info.getTitle() != null) {
@ -211,9 +237,10 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
// when projectDescription is not specified, use info.description
projectDescription = info.getDescription();
}
if (info.getLicense() != null) {
License license = info.getLicense();
if (additionalProperties.get(PROJECT_LICENSE_NAME) == null) {
if (additionalProperties.get(PROJECT_LICENSE_NAME) == null) {
// when projectLicense is not specified, use info.license
if (info.getLicense() != null) {
License license = info.getLicense();
additionalProperties.put(PROJECT_LICENSE_NAME, license.getName());
}
}
@ -237,10 +264,14 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
additionalProperties.put(MODULE_NAME, moduleName);
additionalProperties.put(PROJECT_DESCRIPTION, escapeText(projectDescription));
additionalProperties.put(PROJECT_VERSION, projectVersion);
additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage);
additionalProperties.put(CodegenConstants.LOCAL_VARIABLE_PREFIX, localVariablePrefix);
additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage);
additionalProperties.put(CodegenConstants.SOURCE_FOLDER, sourceFolder);
additionalProperties.put(USE_PROMISES, usePromises);
additionalProperties.put(OMIT_MODEL_METHODS, omitModelMethods);
additionalProperties.put(USE_INHERITANCE, supportsInheritance);
additionalProperties.put(EMIT_MODEL_METHODS, emitModelMethods);
additionalProperties.put(EMIT_JS_DOC, emitJSDoc);
// make api and model doc path available in mustache template
additionalProperties.put("apiDocPath", apiDocPath);
@ -260,12 +291,56 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
@Override
public String apiFileFolder() {
return outputFolder + "/" + sourceFolder + "/" + apiPackage().replace('.', File.separatorChar);
return outputFolder + '/' + sourceFolder + '/' + apiPackage().replace('.', '/');
}
@Override
public String modelFileFolder() {
return outputFolder + "/" + sourceFolder + "/" + modelPackage().replace('.', File.separatorChar);
return outputFolder + '/' + sourceFolder + '/' + modelPackage().replace('.', '/');
}
public void setSourceFolder(String sourceFolder) {
this.sourceFolder = sourceFolder;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public void setLocalVariablePrefix(String localVariablePrefix) {
this.localVariablePrefix = localVariablePrefix;
}
public void setModuleName(String moduleName) {
this.moduleName = moduleName;
}
public void setProjectDescription(String projectDescription) {
this.projectDescription = projectDescription;
}
public void setProjectVersion(String projectVersion) {
this.projectVersion = projectVersion;
}
public void setProjectLicenseName(String projectLicenseName) {
this.projectLicenseName = projectLicenseName;
}
public void setUsePromises(boolean usePromises) {
this.usePromises = usePromises;
}
public void setUseInheritance(boolean useInheritance) {
this.supportsInheritance = useInheritance;
}
public void setEmitModelMethods(boolean emitModelMethods) {
this.emitModelMethods = emitModelMethods;
}
public void setEmitJSDoc(boolean emitJSDoc) {
this.emitJSDoc = emitJSDoc;
}
@Override
@ -607,6 +682,92 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
return codegenModel;
}
private String trimBrackets(String s) {
if (s != null) {
int beginIdx = s.charAt(0) == '[' ? 1 : 0;
int endIdx = s.length();
if (s.charAt(endIdx - 1) == ']')
endIdx--;
return s.substring(beginIdx, endIdx);
}
return null;
}
private String getModelledType(String dataType) {
return "module:" + (StringUtils.isEmpty(modelPackage) ? "" : (modelPackage + "/")) + dataType;
}
private String getJSDocTypeWithBraces(CodegenModel cm, CodegenProperty cp) {
return "{" + getJSDocType(cm, cp) + "}";
}
private String getJSDocType(CodegenModel cm, CodegenProperty cp) {
if (Boolean.TRUE.equals(cp.isContainer)) {
if (cp.containerType.equals("array"))
return "Array.<" + getJSDocType(cm, cp.items) + ">";
else if (cp.containerType.equals("map"))
return "Object.<String, " + getJSDocType(cm, cp.items) + ">";
}
String dataType = trimBrackets(cp.datatypeWithEnum);
if (cp.isEnum) {
dataType = cm.classname + '.' + dataType;
}
if (isModelledType(cp))
dataType = getModelledType(dataType);
return dataType;
}
private boolean isModelledType(CodegenProperty cp) {
// N.B. enums count as modelled types, file is not modelled (SuperAgent uses some 3rd party library).
return cp.isEnum || !languageSpecificPrimitives.contains(cp.baseType == null ? cp.datatype : cp.baseType);
}
private String getJSDocTypeWithBraces(CodegenParameter cp) {
return "{" + getJSDocType(cp) + "}";
}
private String getJSDocType(CodegenParameter cp) {
String dataType = trimBrackets(cp.dataType);
if (isModelledType(cp))
dataType = getModelledType(dataType);
if (Boolean.TRUE.equals(cp.isListContainer)) {
return "Array.<" + dataType + ">";
} else if (Boolean.TRUE.equals(cp.isMapContainer)) {
return "Object.<String, " + dataType + ">";
}
return dataType;
}
private boolean isModelledType(CodegenParameter cp) {
// N.B. enums count as modelled types, file is not modelled (SuperAgent uses some 3rd party library).
return cp.isEnum || !languageSpecificPrimitives.contains(cp.baseType == null ? cp.dataType : cp.baseType);
}
private String getJSDocTypeWithBraces(CodegenOperation co) {
String jsDocType = getJSDocType(co);
return jsDocType == null ? null : "{" + jsDocType + "}";
}
private String getJSDocType(CodegenOperation co) {
String returnType = trimBrackets(co.returnType);
if (returnType != null) {
if (isModelledType(co))
returnType = getModelledType(returnType);
if (Boolean.TRUE.equals(co.isListContainer)) {
return "Array.<" + returnType + ">";
} else if (Boolean.TRUE.equals(co.isMapContainer)) {
return "Object.<String, " + returnType + ">";
}
}
return returnType;
}
private boolean isModelledType(CodegenOperation co) {
// This seems to be the only way to tell whether an operation return type is modelled.
return !Boolean.TRUE.equals(co.returnTypeIsPrimitive);
}
@SuppressWarnings("unchecked")
@Override
public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
// Generate and store argument list string of each operation into
@ -615,7 +776,7 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
if (operations != null) {
List<CodegenOperation> ops = (List<CodegenOperation>) operations.get("operation");
for (CodegenOperation operation : ops) {
List<String> argList = new ArrayList();
List<String> argList = new ArrayList<String>();
boolean hasOptionalParams = false;
for (CodegenParameter p : operation.allParams) {
if (p.required != null && p.required) {
@ -631,20 +792,46 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
argList.add("callback");
}
operation.vendorExtensions.put("x-codegen-argList", StringUtils.join(argList, ", "));
// Store JSDoc type specification into vendor-extension: x-jsdoc-type.
for (CodegenParameter cp : operation.allParams) {
String jsdocType = getJSDocTypeWithBraces(cp);
cp.vendorExtensions.put("x-jsdoc-type", jsdocType);
}
String jsdocType = getJSDocTypeWithBraces(operation);
operation.vendorExtensions.put("x-jsdoc-type", jsdocType);
}
}
return objs;
}
@SuppressWarnings("unchecked")
@Override
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
List<Object> models = (List<Object>) objs.get("models");
for (Object _mo : models) {
Map<String, Object> mo = (Map<String, Object>) _mo;
CodegenModel cm = (CodegenModel) mo.get("model");
// Collect each model's required property names in *document order*.
// NOTE: can't use 'mandatory' as it is built from ModelImpl.getRequired(), which sorts names
// alphabetically and in any case the document order of 'required' and 'properties' can differ.
List<String> required = new ArrayList<String>();
List<String> allRequired = supportsInheritance ? new ArrayList<String>() : required;
cm.vendorExtensions.put("x-required", required);
cm.vendorExtensions.put("x-all-required", allRequired);
for (CodegenProperty var : cm.vars) {
Map<String, Object> allowableValues = var.allowableValues;
// Add JSDoc @type value for this property.
String jsDocType = getJSDocTypeWithBraces(cm, var);
var.vendorExtensions.put("x-jsdoc-type", jsDocType);
if (Boolean.TRUE.equals(var.required)) {
required.add(var.name);
}
// handle ArrayProperty
if (var.items != null) {
allowableValues = var.items.allowableValues;
@ -679,6 +866,15 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
}
allowableValues.put("enumVars", enumVars);
}
if (supportsInheritance) {
for (CodegenProperty var : cm.allVars) {
if (Boolean.TRUE.equals(var.required)) {
allRequired.add(var.name);
}
}
}
// set vendor-extension: x-codegen-hasMoreRequired
CodegenProperty lastRequired = null;
for (CodegenProperty var : cm.vars) {

View File

@ -15,46 +15,74 @@
}(this, function(superagent) {
'use strict';
var ApiClient = function ApiClient() {
/**
* The base path to put in front of every API call's (relative) path.
{{#emitJSDoc}} /**
* @module ApiClient
* @version {{projectVersion}}
*/
/**
* Manages low level client-server communications, parameter marshalling, etc. There should not be any need for an
* application to use this class directly - the *Api and model classes provide the public API for the service. The
* contents of this file should be regarded as internal but are documented for completeness.
* @alias module:ApiClient
* @class
*/
{{/emitJSDoc}} var exports = function() {
{{#emitJSDoc}} /**
* The base URL against which to resolve every API call's (relative) path.
* @type {String}
* @default {{basePath}}
*/
this.basePath = '{{basePath}}'.replace(/\/+$/, '');
{{=< >=}}
this.authentications = {<#authMethods><#isBasic>
{{/emitJSDoc}} this.basePath = '{{basePath}}'.replace(/\/+$/, '');
{{#emitJSDoc}} /**
* The authentication methods to be included for all API calls.
* @type {Array.<String>}
*/
{{/emitJSDoc}}{{=< >=}} this.authentications = {<#authMethods><#isBasic>
'<name>': {type: 'basic'}</isBasic><#isApiKey>
'<name>': {type: 'apiKey', in: <#isKeyInHeader>'header'</isKeyInHeader><^isKeyInHeader>'query'</isKeyInHeader>, name: '<keyParamName>'}</isApiKey><#isOAuth>
'<name>': {type: 'apiKey', 'in': <#isKeyInHeader>'header'</isKeyInHeader><^isKeyInHeader>'query'</isKeyInHeader>, name: '<keyParamName>'}</isApiKey><#isOAuth>
'<name>': {type: 'oauth2'}</isOAuth><#hasMore>,</hasMore></authMethods>
};
<={{ }}=>
/**
{{#emitJSDoc}} /**
* The default HTTP headers to be included for all API calls.
* @type {Array.<String>}
* @default {}
*/
this.defaultHeaders = {};
{{/emitJSDoc}} this.defaultHeaders = {};
/**
* The default HTTP timeout for all API calls.
* @type {Number}
* @default 60000
*/
this.timeout = 60000;
};
ApiClient.prototype.paramToString = function paramToString(param) {
if (param == null) {
// return empty string for null and undefined
{{#emitJSDoc}} /**
* Returns a string representation for an actual parameter.
* @param param The actual parameter.
* @returns {String} The string representation of <code>param</code>.
*/
{{/emitJSDoc}} exports.prototype.paramToString = function(param) {
if (param == undefined || param == null) {
return '';
} else if (param instanceof Date) {
return param.toJSON();
} else {
return param.toString();
}
if (param instanceof Date) {
return param.toJSON();
}
return param.toString();
};
/**
* Build full URL by appending the given path to base path and replacing
* path parameter placeholders with parameter values.
{{#emitJSDoc}} /**
* Builds full URL by appending the given path to the base URL and replacing path parameter place-holders with parameter values.
* NOTE: query parameters are not handled here.
* @param {String} path The path to append to the base URL.
* @param {Object} pathParams The parameter values to append.
* @returns {String} The encoded path with parameter values substituted.
*/
ApiClient.prototype.buildUrl = function buildUrl(path, pathParams) {
{{/emitJSDoc}} exports.prototype.buildUrl = function(path, pathParams) {
if (!path.match(/^\//)) {
path = '/' + path;
}
@ -72,35 +100,41 @@
return url;
};
/**
* Check if the given MIME is a JSON MIME.
* JSON MIME examples:
* application/json
* application/json; charset=UTF8
* APPLICATION/JSON
{{#emitJSDoc}} /**
* Checks whether the given content type represents JSON.<br>
* JSON content type examples:<br>
* <ul>
* <li>application/json</li>
* <li>application/json; charset=UTF8</li>
* <li>APPLICATION/JSON</li>
* </ul>
* @param {String} contentType The MIME content type to check.
* @returns {Boolean} <code>true</code> if <code>contentType</code> represents JSON, otherwise <code>false</code>.
*/
ApiClient.prototype.isJsonMime = function isJsonMime(mime) {
return Boolean(mime != null && mime.match(/^application\/json(;.*)?$/i));
{{/emitJSDoc}} exports.prototype.isJsonMime = function(contentType) {
return Boolean(contentType != null && contentType.match(/^application\/json(;.*)?$/i));
};
/**
* Choose a MIME from the given MIMEs with JSON preferred,
* i.e. return JSON if included, otherwise return the first one.
{{#emitJSDoc}} /**
* Chooses a content type from the given array, with JSON preferred; i.e. return JSON if included, otherwise return the first.
* @param {Array.<String>} contentTypes
* @returns {String} The chosen content type, preferring JSON.
*/
ApiClient.prototype.jsonPreferredMime = function jsonPreferredMime(mimes) {
var len = mimes.length;
for (var i = 0; i < len; i++) {
if (this.isJsonMime(mimes[i])) {
return mimes[i];
{{/emitJSDoc}} exports.prototype.jsonPreferredMime = function(contentTypes) {
for (var i = 0; i < contentTypes.length; i++) {
if (this.isJsonMime(contentTypes[i])) {
return contentTypes[i];
}
}
return mimes[0];
return contentTypes[0];
};
/**
* Check if the given parameter value is like file content.
{{#emitJSDoc}} /**
* Checks whether the given parameter value represents file-like content.
* @param param The parameter to check.
* @returns {Boolean} <code>true</code> if <code>param</code> represents a file.
*/
ApiClient.prototype.isFileParam = function isFileParam(param) {
{{/emitJSDoc}} exports.prototype.isFileParam = function(param) {
// fs.ReadStream in Node.js (but not in runtime like browserify)
if (typeof window === 'undefined' &&
typeof require === 'function' &&
@ -123,16 +157,20 @@
return false;
};
/**
* Normalize parameters values:
* remove nils,
* keep files and arrays,
* format to string with `paramToString` for other cases.
{{#emitJSDoc}} /**
* Normalizes parameter values:
* <ul>
* <li>remove nils</li>
* <li>keep files and arrays</li>
* <li>format to string with `paramToString` for other cases</li>
* </ul>
* @param {Object.<String, Object>} params The parameters as object properties.
* @returns {Object.<String, Object>} normalized parameters.
*/
ApiClient.prototype.normalizeParams = function normalizeParams(params) {
{{/emitJSDoc}} exports.prototype.normalizeParams = function(params) {
var newParams = {};
for (var key in params) {
if (params.hasOwnProperty(key) && params[key] != null) {
if (params.hasOwnProperty(key) && params[key] != undefined && params[key] != null) {
var value = params[key];
if (this.isFileParam(value) || Array.isArray(value)) {
newParams[key] = value;
@ -144,11 +182,47 @@
return newParams;
};
/**
* Build parameter value according to the given collection format.
* @param {String} collectionFormat one of 'csv', 'ssv', 'tsv', 'pipes' and 'multi'
{{#emitJSDoc}} /**
* Enumeration of collection format separator strategies.
* @enum {String}
* @readonly
*/
ApiClient.prototype.buildCollectionParam = function buildCollectionParam(param, collectionFormat) {
exports.CollectionFormatEnum = {
/**
* Comma-separated values. Value: <code>csv</code>
* @const
*/
CSV: ',',
/**
* Space-separated values. Value: <code>ssv</code>
* @const
*/
SSV: ' ',
/**
* Tab-separated values. Value: <code>tsv</code>
* @const
*/
TSV: '\t',
/**
* Pipe(|)-separated values. Value: <code>pipes</code>
* @const
*/
PIPES: '|',
/**
* Native array. Value: <code>multi</code>
* @const
*/
MULTI: 'multi'
};
/**
* Builds a string representation of an array-type actual parameter, according to the given collection format.
* @param {Array} param An array parameter.
* @param {module:ApiClient.CollectionFormatEnum} collectionFormat The array element separator strategy.
* @returns {String|Array} A string representation of the supplied collection, using the specified delimiter. Returns
* <code>param</code> as is if <code>collectionFormat</code> is <code>multi</code>.
*/
{{/emitJSDoc}} exports.prototype.buildCollectionParam = function buildCollectionParam(param, collectionFormat) {
if (param == null) {
return null;
}
@ -162,14 +236,19 @@
case 'pipes':
return param.map(this.paramToString).join('|');
case 'multi':
// return the array directly as Superagent will handle it as expected
// return the array directly as SuperAgent will handle it as expected
return param.map(this.paramToString);
default:
throw new Error('Unknown collection format: ' + collectionFormat);
}
};
ApiClient.prototype.applyAuthToRequest = function applyAuthToRequest(request, authNames) {
{{#emitJSDoc}} /**
* Applies authentication headers to the request.
* @param {Object} request The request object created by a <code>superagent()</code> call.
* @param {Array.<String>} authNames An array of authentication method names.
*/
{{/emitJSDoc}} exports.prototype.applyAuthToRequest = function(request, authNames) {
var _this = this;
authNames.forEach(function(authName) {
var auth = _this.authentications[authName];
@ -187,7 +266,7 @@
} else {
data[auth.name] = auth.apiKey;
}
if (auth.in === 'header') {
if (auth['in'] === 'header') {
request.set(data);
} else {
request.query(data);
@ -205,24 +284,58 @@
});
};
ApiClient.prototype.deserialize = function deserialize(response, returnType) {
{{#emitJSDoc}} /**
* Deserializes an HTTP response body into a value of the specified type.
* @param {Object} response A SuperAgent response object.
* @param {(String|Array.<String>|Object.<String, Object>|Function)} returnType The type to return. Pass a string for simple types
* or the constructor function for a complex type. Pass an array containing the type name to return an array of that type. To
* return an object, pass an object with one property whose name is the key type and whose value is the corresponding value type:
* all properties on <code>data<code> will be converted to this type.
* @returns A value of the specified type.
*/
{{/emitJSDoc}} exports.prototype.deserialize = function deserialize(response, returnType) {
if (response == null || returnType == null) {
return null;
}
// Rely on Superagent for parsing response body.
// Rely on SuperAgent for parsing response body.
// See http://visionmedia.github.io/superagent/#parsing-response-bodies
var data = response.body;
if (data == null) {
// Superagent does not always produce a body; use the unparsed response
// as a fallback
// SuperAgent does not always produce a body; use the unparsed response as a fallback
data = response.text;
}
return ApiClient.convertToType(data, returnType);
return exports.convertToType(data, returnType);
};
ApiClient.prototype.callApi = function callApi(path, httpMethod, pathParams,
queryParams, headerParams, formParams, bodyParam, authNames, contentTypes,
accepts, returnType{{^usePromises}}, callback{{/usePromises}}) {
{{#emitJSDoc}}{{^usePromises}} /**
* Callback function to receive the result of the operation.
* @callback module:ApiClient~callApiCallback
* @param {String} error Error message, if any.
* @param data The data returned by the service call.
* @param {String} response The complete HTTP response.
*/
{{/usePromises}} /**
* Invokes the REST service using the supplied settings and parameters.
* @param {String} path The base URL to invoke.
* @param {String} httpMethod The HTTP method to use.
* @param {Object.<String, String>} pathParams A map of path parameters and their values.
* @param {Object.<String, Object>} queryParams A map of query parameters and their values.
* @param {Object.<String, Object>} headerParams A map of header parameters and their values.
* @param {Object.<String, Object>} formParams A map of form parameters and their values.
* @param {Object} bodyParam The value to pass as the request body.
* @param {Array.<String>} authNames An array of authentication type names.
* @param {Array.<String>} contentTypes An array of request MIME types.
* @param {Array.<String>} accepts An array of acceptable response MIME types.
* @param {(String|Array|ObjectFunction)} returnType The required type to return; can be a string for simple types or the
* constructor for a complex type.{{^usePromises}}
* @param {module:ApiClient~callApiCallback} callback The callback function.
{{/usePromises}} * @returns {{#usePromises}}{Promise} A Promise object{{/usePromises}}{{^usePromises}}{Object} The SuperAgent request object{{/usePromises}}.
*/
{{/emitJSDoc}} exports.prototype.callApi = function callApi(path, httpMethod, pathParams,
queryParams, headerParams, formParams, bodyParam, authNames, contentTypes, accepts,
returnType{{^usePromises}}, callback{{/usePromises}}) {
var _this = this;
var url = this.buildUrl(path, pathParams);
var request = superagent(httpMethod, url);
@ -236,7 +349,7 @@
// set header parameters
request.set(this.defaultHeaders).set(this.normalizeParams(headerParams));
//set request timeout
// set request timeout
request.timeout(this.timeout);
var contentType = this.jsonPreferredMime(contentTypes);
@ -269,21 +382,17 @@
request.accept(accept);
}
{{#usePromises}}
return new Promise( function(resolve,reject) {
{{#usePromises}} return new Promise(function(resolve, reject) {
request.end(function(error, response) {
if (error) {
reject(error);
}
else {
} else {
var data = _this.deserialize(response, returnType);
resolve(data);
}
});
});
{{/usePromises}}
{{^usePromises}}
request.end(function(error, response) {
});{{/usePromises}}
{{^usePromises}} request.end(function(error, response) {
if (callback) {
var data = null;
if (!error) {
@ -294,15 +403,27 @@
});
return request;
{{/usePromises}}
{{/usePromises}} };
{{#emitJSDoc}} /**
* Parses an ISO-8601 string representation of a date value.
* @param {String} str The date value as a string.
* @returns {Date} The parsed date object.
*/
{{/emitJSDoc}} exports.parseDate = function(str) {
return new Date(str.replace(/T/i, ' '));
};
ApiClient.parseDate = function parseDate(str) {
str = str.replace(/T/i, ' ');
return new Date(str);
};
ApiClient.convertToType = function convertToType(data, type) {
{{#emitJSDoc}} /**
* Converts a value to the specified type.
* @param {(String|Object)} data The data to convert, as a string or object.
* @param {(String|Array.<String>|Object.<String, Object>|Function)} type The type to return. Pass a string for simple types
* or the constructor function for a complex type. Pass an array containing the type name to return an array of that type. To
* return an object, pass an object with one property whose name is the key type and whose value is the corresponding value type:
* all properties on <code>data<code> will be converted to this type.
* @returns An instance of the specified type.
*/
{{/emitJSDoc}} exports.convertToType = function(data, type) {
switch (type) {
case 'Boolean':
return Boolean(data);
@ -320,13 +441,12 @@
return data;
} else if (typeof type === 'function') {
// for model type like: User
var model = type.constructFromObject(data);
return model;
return type.constructFromObject(data);
} else if (Array.isArray(type)) {
// for array type like: ['String']
var itemType = type[0];
return data.map(function(item) {
return ApiClient.convertToType(item, itemType);
return exports.convertToType(item, itemType);
});
} else if (typeof type === 'object') {
// for plain object type like: {'String': 'Integer'}
@ -341,8 +461,8 @@
var result = {};
for (var k in data) {
if (data.hasOwnProperty(k)) {
var key = ApiClient.convertToType(k, keyType);
var value = ApiClient.convertToType(data[k], valueType);
var key = exports.convertToType(k, keyType);
var value = exports.convertToType(data[k], valueType);
result[key] = value;
}
}
@ -354,7 +474,11 @@
}
};
ApiClient.default = new ApiClient();
{{#emitJSDoc}} /**
* The default API client implementation.
* @type {module:ApiClient}
*/
{{/emitJSDoc}} exports.instance = new exports();
return ApiClient;
return exports;
}));

View File

@ -1,10 +1,10 @@
{{=< >=}}(function(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['../ApiClient'<#imports>, '../model/<import>'</imports>], factory);
define(['../ApiClient'<#imports>, '../<#modelPackage><modelPackage>/</modelPackage><import>'</imports>], factory);
} else if (typeof module === 'object' && module.exports) {
// CommonJS-like environments that support module.exports, like Node.
module.exports = factory(require('../ApiClient')<#imports>, require('../model/<import>')</imports>);
module.exports = factory(require('../ApiClient')<#imports>, require('../<#modelPackage><modelPackage>/</modelPackage><import>')</imports>);
} else {
// Browser globals (root is window)
if (!root.<moduleName>) {
@ -15,28 +15,50 @@
}(this, function(ApiClient<#imports>, <import></imports>) {
'use strict';
var <classname> = function <classname>(apiClient) {
this.apiClient = apiClient || ApiClient.default;
<#emitJSDoc> /**
* <baseName> service.
* @module <#apiPackage><apiPackage>/</apiPackage><classname>
* @version <projectVersion>
*/
var self = this;
<#operations>
<#operation>
/**
* Constructs a new <classname>. <#description>
* <description></description>
* @alias module:<#apiPackage><apiPackage>/</apiPackage><classname>
* @class
* @param {module:ApiClient} apiClient Optional API client implementation to use, defaulting to {@link module:ApiClient#instance}
* if unspecified.
*/
</emitJSDoc> var exports = function(apiClient) {
this.apiClient = apiClient || ApiClient.instance;
<#operations><#operation><#emitJSDoc><^usePromises>
/**
* <summary>
* <notes><#allParams>
* @param {<dataType>} <#required><paramName></required><^required>opts['<paramName>']</required> <description></allParams><^usePromises>
* @param {function} callback the callback function, accepting three arguments: error, data, response</usePromises><#returnType>
* data is of type: <&returnType></returnType>
* Callback function to receive the result of the <nickname> operation.
* @callback module:<#apiPackage><apiPackage>/</apiPackage><classname>~<nickname>Callback
* @param {String} error Error message, if any.
* @param <#vendorExtensions.x-jsdoc-type><&vendorExtensions.x-jsdoc-type> data The data returned by the service call.</vendorExtensions.x-jsdoc-type><^vendorExtensions.x-jsdoc-type>data This operation does not return a value.</vendorExtensions.x-jsdoc-type>
* @param {String} response The complete HTTP response.
*/</usePromises>
/**<#summary>
* <summary></summary><#notes>
* <notes></notes><#allParams><#required>
* @param <&vendorExtensions.x-jsdoc-type> <paramName> <description></required></allParams><#hasOptionalParams>
* @param {Object} opts Optional parameters<#allParams><^required>
* @param <&vendorExtensions.x-jsdoc-type> opts.<paramName> <description></required></allParams></hasOptionalParams><^usePromises>
* @param {module:<#apiPackage><apiPackage>/</apiPackage><classname>~<nickname>Callback} callback The callback function, accepting three arguments: error, data, response</usePromises><#returnType>
* data is of type: <&vendorExtensions.x-jsdoc-type></returnType>
*/
self.<nickname> = function(<vendorExtensions.x-codegen-argList>) {<#hasOptionalParams>
</emitJSDoc> this.<nickname> = function(<vendorExtensions.x-codegen-argList>) {<#hasOptionalParams>
opts = opts || {};</hasOptionalParams>
var postBody = <#bodyParam><#required><paramName></required><^required>opts['<paramName>']</required></bodyParam><^bodyParam>null</bodyParam>;
<#allParams><#required>
<#allParams><#required>
// verify the required parameter '<paramName>' is set
if (<paramName> == null) {
if (<paramName> == undefined || <paramName> == null) {
throw "Missing the required parameter '<paramName>' when calling <nickname>";
}
</required></allParams>
</required></allParams>
var pathParams = {<#pathParams>
'<baseName>': <#required><paramName></required><^required>opts['<paramName>']</required><#hasMore>,</hasMore></pathParams>
@ -61,11 +83,8 @@
pathParams, queryParams, headerParams, formParams, postBody,
authNames, contentTypes, accepts, returnType<^usePromises>, callback</usePromises>
);
}
</operation>
</operations>
};
</operation></operations> };
return <classname>;
return exports;
}));<={{ }}=>

View File

@ -1,10 +1,13 @@
var {{datatypeWithEnum}} = {
{{#allowableValues}}{{#enumVars}}
/**
* @const
*/
{{name}}: "{{value}}"{{^-last}},
{{/-last}}{{/enumVars}}{{/allowableValues}}
};
{{classname}}.{{datatypeWithEnum}} = {{datatypeWithEnum}};
{{#emitJSDoc}} /**
* Allowed values for the <code>{{baseName}}</code> property.
* @enum {{=<% %>=}}{<%datatype%>}<%={{ }}=%>
* @readonly
*/{{/emitJSDoc}}
exports.{{datatypeWithEnum}} = { {{#allowableValues}}{{#enumVars}}
{{#emitJSDoc}} /**
* value: {{value}}
* @const
*/
{{/emitJSDoc}} {{name}}: "{{value}}"{{^-last}},
{{/-last}}{{/enumVars}}{{/allowableValues}}
};

View File

@ -1,17 +1,62 @@
(function(factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['./ApiClient'{{#models}}, './model/{{importPath}}'{{/models}}{{#apiInfo}}{{#apis}}, './api/{{importPath}}'{{/apis}}{{/apiInfo}}], factory);
define(['./ApiClient'{{#models}}, './{{#modelPackage}}{{modelPackage}}/{{/modelPackage}}{{importPath}}'{{/models}}{{#apiInfo}}{{#apis}}, './{{#apiPackage}}{{apiPackage}}/{{/apiPackage}}{{importPath}}'{{/apis}}{{/apiInfo}}], factory);
} else if (typeof module === 'object' && module.exports) {
// CommonJS-like environments that support module.exports, like Node.
module.exports = factory(require('./ApiClient'){{#models}}, require('./model/{{importPath}}'){{/models}}{{#apiInfo}}{{#apis}}, require('./api/{{importPath}}'){{/apis}}{{/apiInfo}});
module.exports = factory(require('./ApiClient'){{#models}}, require('./{{#modelPackage}}{{modelPackage}}/{{/modelPackage}}{{importPath}}'){{/models}}{{#apiInfo}}{{#apis}}, require('./{{#apiPackage}}{{apiPackage}}/{{/apiPackage}}{{importPath}}'){{/apis}}{{/apiInfo}});
}
}(function(ApiClient{{#models}}{{#model}}, {{classFilename}}{{/model}}{{/models}}{{#apiInfo}}{{#apis}}, {{importPath}}{{/apis}}{{/apiInfo}}) {
'use strict';
return {
ApiClient: ApiClient{{#models}},
{{importPath}}: {{importPath}}{{/models}}{{#apiInfo}}{{#apis}},
{{importPath}}: {{importPath}}{{/apis}}{{/apiInfo}}
{{#emitJSDoc}} /**{{#projectDescription}}
* {{projectDescription}}.<br>{{/projectDescription}}
* The <code>index</code> module provides access to constructors for all the classes which comprise the public API.
* <p>
* An AMD (recommended!) or CommonJS application will generally do something equivalent to the following:
* <pre>
* var {{moduleName}} = require('./index'); // See note below*.
* var xxxSvc = new {{moduleName}}.XxxApi(); // Allocate the API class we're going to use.
* var yyyModel = new {{moduleName}}.Yyy(); // Construct a model instance.
* yyyModel.someProperty = 'someValue';
* ...
* var zzz = xxxSvc.doSomething(yyyModel); // Invoke the service.
* ...
* </pre>
* <em>*NOTE: For a top-level AMD script, use require(['./index'], function(){...}) and put the application logic within the
* callback function.</em>
* </p>
* <p>
* A non-AMD browser application (discouraged) might do something like this:
* <pre>
* var xxxSvc = new {{moduleName}}.XxxApi(); // Allocate the API class we're going to use.
* var yyy = new {{moduleName}}.Yyy(); // Construct a model instance.
* yyyModel.someProperty = 'someValue';
* ...
* var zzz = xxxSvc.doSomething(yyyModel); // Invoke the service.
* ...
* </pre>
* </p>
* @module index
* @version {{projectVersion}}
*/{{/emitJSDoc}}
{{=< >=}} var exports = {<#emitJSDoc>
/**
* The ApiClient constructor.
* @property {module:ApiClient}
*/</emitJSDoc>
ApiClient: ApiClient<#models>,<#emitJSDoc>
/**
* The <importPath> model constructor.
* @property {module:<#modelPackage><modelPackage>/</modelPackage><importPath>}
*/</emitJSDoc>
<importPath>: <importPath></models><#apiInfo><#apis>,<#emitJSDoc>
/**
* The <importPath> service constructor.
* @property {module:<#apiPackage><apiPackage>/</apiPackage><importPath>}
*/</emitJSDoc>
<importPath>: <importPath></apis></apiInfo>
};
return exports;<={{ }}=>
}));

View File

@ -14,69 +14,87 @@
}
}(this, function(ApiClient{{#imports}}, {{import}}{{/imports}}) {
'use strict';
{{#models}}{{#model}}
{{#description}}/**
* {{description}}
**/{{/description}}
var {{classname}} = function {{classname}}({{#vars}}{{#required}}{{name}}{{#vendorExtensions.x-codegen-hasMoreRequired}}, {{/vendorExtensions.x-codegen-hasMoreRequired}}{{/required}}{{/vars}}) { {{#parent}}/* extends {{{parent}}}*/{{/parent}}
{{#vars}}{{#required}}
/**{{#description}}
* {{{description}}}{{/description}}
* datatype: {{{datatypeWithEnum}}}
* required {{#minimum}}
* minimum: {{minimum}}{{/minimum}}{{#maximum}}
* maximum: {{maximum}}{{/maximum}}
**/
this['{{baseName}}'] = {{name}};{{/required}}{{^required}}{{#defaultValue}}
/**{{#description}}
* {{{description}}}{{/description}}
* datatype: {{{datatypeWithEnum}}}
* required {{#minimum}}
* minimum: {{minimum}}{{/minimum}}{{#maximum}}
* maximum: {{maximum}}{{/maximum}}
**/
this['{{baseName}}'] = {{{defaultValue}}};{{/defaultValue}}{{/required}}{{/vars}}
};
{{classname}}.constructFromObject = function(data) {
if (!data) {
return null;
}
var _this = new {{classname}}();
{{#vars}}
if (data['{{baseName}}']) {
_this['{{baseName}}']{{{defaultValueWithParam}}}
}
{{/vars}}
return _this;
{{#models}}{{#model}}{{#emitJSDoc}} /**
* The {{classname}} model module.
* @module {{#modelPackage}}{{modelPackage}}/{{/modelPackage}}{{classname}}
* @version {{projectVersion}}
*/
/**
* Constructs a new <code>{{classname}}</code>.{{#description}}
* {{description}}{{/description}}
* @alias module:{{#modelPackage}}{{modelPackage}}/{{/modelPackage}}{{classname}}
* @class{{#useInheritance}}{{#parent}}
* @extends module:{{#modelPackage}}{{modelPackage}}/{{/modelPackage}}{{parent}}{{/parent}}{{#interfaces}}
* @implements module:{{#modelPackage}}{{modelPackage}}/{{/modelPackage}}{{.}}{{/interfaces}}{{/useInheritance}}{{#vendorExtensions.x-all-required}}
* @param {{.}}{{/vendorExtensions.x-all-required}}
*/{{/emitJSDoc}}
var exports = function({{#vendorExtensions.x-all-required}}{{.}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-all-required}}) {
{{#useInheritance}}{{#parentModel}} {{classname}}.call(this{{#vendorExtensions.x-all-required}}, {{.}}{{/vendorExtensions.x-all-required}});{{/parentModel}}
{{#interfaceModels}} {{classname}}.call(this{{#vendorExtensions.x-all-required}}, {{.}}{{/vendorExtensions.x-all-required}});
{{/interfaceModels}}{{/useInheritance}}{{#vars}}{{#required}} this['{{baseName}}'] = {{name}};{{/required}}
{{/vars}} };
{{#emitJSDoc}} /**
* Constructs a <code>{{classname}}</code> from a plain JavaScript object, optionally creating a new instance.
* Copies all relevant properties from <code>data</code> to <code>obj</code> if supplied or a new instance if not.
* @param {Object} data The plain JavaScript object bearing properties of interest.
* @param {{=< >=}}{module:<#modelPackage><modelPackage>/</modelPackage><classname>}<={{ }}=> obj Optional instance to populate.
* @return {{=< >=}}{module:<#modelPackage><modelPackage>/</modelPackage><classname>}<={{ }}=> The populated <code>{{classname}}</code> instance.
*/
{{/emitJSDoc}} exports.constructFromObject = function(data, obj) {
if (data) { {{!// TODO: support polymorphism: discriminator property on data determines class to instantiate.}}
obj = obj || new exports();
{{#useInheritance}}{{#parent}} {{.}}.constructFromObject(data, obj);{{/parent}}
{{#interfaces}} {{.}}.constructFromObject(data, obj);
{{/interfaces}}{{/useInheritance}}{{#vars}} if (data.hasOwnProperty('{{baseName}}')) {
obj['{{baseName}}']{{{defaultValueWithParam}}}
}
{{/vars}} }
return obj;
}
{{^omitModelMethods}}
{{#vars}}
{{#useInheritance}}{{#parent}}
exports.prototype = Object.create({{parent}}.prototype);
exports.prototype.constructor = exports;
{{/parent}}{{/useInheritance}}
{{#vars}}{{#emitJSDoc}}
/**{{#description}}
* get {{{description}}}{{/description}}{{#minimum}}
* {{{description}}}{{/description}}
* @member {{{vendorExtensions.x-jsdoc-type}}} {{baseName}}{{#defaultValue}}
* @default {{{defaultValue}}}{{/defaultValue}}
*/
{{/emitJSDoc}} exports.prototype['{{baseName}}'] = {{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}undefined{{/defaultValue}};
{{/vars}}{{#useInheritance}}{{#interfaceModels}}
// Implement {{classname}} interface:{{#allVars}}{{#emitJSDoc}}
/**{{#description}}
* {{{description}}}{{/description}}
* @member {{{vendorExtensions.x-jsdoc-type}}} {{baseName}}{{#defaultValue}}
* @default {{{defaultValue}}}{{/defaultValue}}
*/
{{/emitJSDoc}} exports.prototype['{{baseName}}'] = {{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}undefined{{/defaultValue}};
{{/allVars}}{{/interfaceModels}}{{/useInheritance}}
{{#emitModelMethods}}{{#vars}}{{#emitJSDoc}} /**{{#description}}
* Returns {{{description}}}{{/description}}{{#minimum}}
* minimum: {{minimum}}{{/minimum}}{{#maximum}}
* maximum: {{maximum}}{{/maximum}}
* @return {{=<% %>=}}{<% datatypeWithEnum %>}<%={{ }}=%>
**/
{{classname}}.prototype.{{getter}} = function() {
* @return {{{vendorExtensions.x-jsdoc-type}}}
*/
{{/emitJSDoc}} exports.prototype.{{getter}} = function() {
return this['{{baseName}}'];
}
/**{{#description}}
* set {{{description}}}{{/description}}
* @param {{=<% %>=}}{<% datatypeWithEnum %>}<%={{ }}=%> {{name}}
**/
{{classname}}.prototype.{{setter}} = function({{name}}) {
{{#emitJSDoc}} /**{{#description}}
* Sets {{{description}}}{{/description}}
* @param {{{vendorExtensions.x-jsdoc-type}}} {{name}}{{#description}} {{{description}}}{{/description}}
*/
{{/emitJSDoc}} exports.prototype.{{setter}} = function({{name}}) {
this['{{baseName}}'] = {{name}};
}
{{/vars}}
{{/omitModelMethods}}
{{#vars}}{{#isEnum}}{{>enumClass}}{{/isEnum}}{{#items.isEnum}}{{#items}}
{{>enumClass}}{{/items}}*/{{/items.isEnum}}{{/vars}}
{{/vars}}{{/emitModelMethods}}
{{#vars}}{{#isEnum}}{{>enumClass}}{{/isEnum}}{{#items.isEnum}}{{#items}}
{{>enumClass}}{{/items}}*/{{/items.isEnum}}{{/vars}}
return {{classname}};
{{/model}}
{{/models}}
}));
return exports;
{{/model}}{{/models}}}));

View File

@ -0,0 +1,71 @@
package io.swagger.codegen.javascript;
import io.swagger.codegen.AbstractOptionsTest;
import io.swagger.codegen.CodegenConfig;
import io.swagger.codegen.options.JavaScriptOptionsProvider;
import io.swagger.codegen.languages.JavascriptClientCodegen;
import io.swagger.codegen.options.OptionsProvider;
import mockit.Expectations;
import mockit.Tested;
public class JavaScriptClientOptionsTest extends AbstractOptionsTest {
@Tested
private JavascriptClientCodegen clientCodegen;
public JavaScriptClientOptionsTest() {
super(new JavaScriptOptionsProvider());
}
protected JavaScriptClientOptionsTest(OptionsProvider optionsProvider) {
super(optionsProvider);
}
@Override
protected CodegenConfig getCodegenConfig() {
return clientCodegen;
}
@Override
protected void setExpectations() {
// Commented generic options not yet supported by JavaScript codegen.
new Expectations(clientCodegen) {{
clientCodegen.setModelPackage(JavaScriptOptionsProvider.MODEL_PACKAGE_VALUE);
times = 1;
clientCodegen.setApiPackage(JavaScriptOptionsProvider.API_PACKAGE_VALUE);
times = 1;
clientCodegen.setSortParamsByRequiredFlag(Boolean.valueOf(JavaScriptOptionsProvider.SORT_PARAMS_VALUE));
times = 1;
// clientCodegen.setInvokerPackage(JavaScriptOptionsProvider.INVOKER_PACKAGE_VALUE);
// times = 1;
// clientCodegen.setGroupId(JavaScriptOptionsProvider.GROUP_ID_VALUE);
// times = 1;
// clientCodegen.setArtifactId(JavaScriptOptionsProvider.ARTIFACT_ID_VALUE);
// times = 1;
// clientCodegen.setArtifactVersion(JavaScriptOptionsProvider.ARTIFACT_VERSION_VALUE);
// times = 1;
clientCodegen.setSourceFolder(JavaScriptOptionsProvider.SOURCE_FOLDER_VALUE);
times = 1;
clientCodegen.setLocalVariablePrefix(JavaScriptOptionsProvider.LOCAL_PREFIX_VALUE);
times = 1;
clientCodegen.setProjectName(JavaScriptOptionsProvider.PROJECT_NAME_VALUE);
times = 1;
clientCodegen.setModuleName(JavaScriptOptionsProvider.MODULE_NAME_VALUE);
times = 1;
clientCodegen.setProjectDescription(JavaScriptOptionsProvider.PROJECT_DESCRIPTION_VALUE);
times = 1;
clientCodegen.setProjectVersion(JavaScriptOptionsProvider.PROJECT_VERSION_VALUE);
times = 1;
clientCodegen.setProjectLicenseName(JavaScriptOptionsProvider.PROJECT_LICENSE_NAME_VALUE);
times = 1;
clientCodegen.setUsePromises(Boolean.valueOf(JavaScriptOptionsProvider.USE_PROMISES_VALUE));
times = 1;
clientCodegen.setUseInheritance(Boolean.valueOf(JavaScriptOptionsProvider.USE_INHERITANCE_VALUE));
times = 1;
clientCodegen.setEmitModelMethods(Boolean.valueOf(JavaScriptOptionsProvider.EMIT_MODEL_METHODS_VALUE));
times = 1;
clientCodegen.setEmitJSDoc(Boolean.valueOf(JavaScriptOptionsProvider.EMIT_JS_DOC_VALUE));
times = 1;
}};
}
}

View File

@ -0,0 +1,102 @@
package io.swagger.codegen.javascript;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.google.common.collect.Sets;
import io.swagger.codegen.CodegenModel;
import io.swagger.codegen.languages.JavascriptClientCodegen;
import io.swagger.models.ComposedModel;
import io.swagger.models.Model;
import io.swagger.models.ModelImpl;
import io.swagger.models.RefModel;
import io.swagger.models.properties.StringProperty;
public class JavaScriptInheritanceTest {
@SuppressWarnings("static-method")
@Test(description = "convert a composed model with inheritance enabled")
public void javascriptInheritanceTest() {
ModelImpl base = new ModelImpl();
base.addProperty("baseProp", new StringProperty().required(true));
ModelImpl intf1 = new ModelImpl();
intf1.addProperty("intf1Prop", new StringProperty());
ModelImpl intf2 = new ModelImpl();
intf2.addProperty("intf2Prop", new StringProperty().required(true));
ModelImpl child = new ModelImpl();
child.addProperty("childProp", new StringProperty().required(true));
final Map<String, Model> allDefinitions = new HashMap<String, Model>();
allDefinitions.put("Base", base);
allDefinitions.put("Interface1", intf1);
allDefinitions.put("Interface2", intf2);
final Model model = new ComposedModel().parent(new RefModel("Base"))
.interfaces(Arrays.asList(new RefModel("Interface1"), new RefModel("Interface2")))
.child(child);
final JavascriptClientCodegen codegen = new JavascriptClientCodegen();
codegen.setUseInheritance(true);
final CodegenModel cm = codegen.fromModel("sample", model, allDefinitions);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.parent, "Base");
Assert.assertEquals(cm.interfaces, Arrays.asList("Interface1", "Interface2"));
Assert.assertEquals(cm.imports, Sets.newHashSet("Base", "Interface1", "Interface2"));
Assert.assertEquals(cm.vars.size(), 1);
Assert.assertEquals(cm.vars.get(0).name, "childProp");
Assert.assertEquals(cm.allVars.size(), 4);
String[] allVars = {"baseProp", "intf1Prop", "intf2Prop", "childProp"};
for (int i = 0; i < allVars.length; i++) {
Assert.assertEquals(cm.allVars.get(i).name, allVars[i]);
}
Assert.assertEquals(cm.mandatory, Sets.newHashSet("childProp"));
Assert.assertEquals(cm.allMandatory, Sets.newHashSet("baseProp", "intf2Prop", "childProp"));
}
@SuppressWarnings("static-method")
@Test(description = "convert a composed model with inheritance disabled")
public void javascriptNoInheritanceTest() {
ModelImpl base = new ModelImpl();
base.addProperty("baseProp", new StringProperty().required(true));
ModelImpl intf1 = new ModelImpl();
intf1.addProperty("intf1Prop", new StringProperty());
ModelImpl intf2 = new ModelImpl();
intf2.addProperty("intf2Prop", new StringProperty().required(true));
ModelImpl child = new ModelImpl();
child.addProperty("childProp", new StringProperty().required(true));
final Map<String, Model> allDefinitions = new HashMap<String, Model>();
allDefinitions.put("Base", base);
allDefinitions.put("Interface1", intf1);
allDefinitions.put("Interface2", intf2);
final Model model = new ComposedModel().parent(new RefModel("Base"))
.interfaces(Arrays.asList(new RefModel("Interface1"), new RefModel("Interface2")))
.child(child);
final JavascriptClientCodegen codegen = new JavascriptClientCodegen();
codegen.setUseInheritance(false);
final CodegenModel cm = codegen.fromModel("sample", model, allDefinitions);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.parent, "Base");
Assert.assertEquals(cm.interfaces, Arrays.asList("Interface1", "Interface2"));
Assert.assertEquals(cm.imports, Sets.newHashSet("Base", "Interface1", "Interface2"));
Assert.assertEquals(cm.vars.size(), 4);
Assert.assertEquals(cm.allVars.size(), 4);
String[] allVars = {"baseProp", "intf1Prop", "intf2Prop", "childProp"};
for (int i = 0; i < allVars.length; i++) {
Assert.assertEquals(cm.vars.get(i).name, allVars[i]);
Assert.assertEquals(cm.allVars.get(i).name, allVars[i]);
}
Assert.assertEquals(cm.mandatory, Sets.newHashSet("baseProp", "intf2Prop", "childProp"));
Assert.assertEquals(cm.allMandatory, Sets.newHashSet("baseProp", "intf2Prop", "childProp"));
}
}

View File

@ -0,0 +1,95 @@
package io.swagger.codegen.javascript;
import io.swagger.codegen.CodegenModel;
import io.swagger.codegen.CodegenProperty;
import io.swagger.codegen.DefaultCodegen;
import io.swagger.codegen.languages.JavaClientCodegen;
import io.swagger.codegen.languages.JavascriptClientCodegen;
import io.swagger.models.ComposedModel;
import io.swagger.models.Model;
import io.swagger.models.ModelImpl;
import io.swagger.models.RefModel;
import io.swagger.models.properties.Property;
import io.swagger.models.properties.StringProperty;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings("static-method")
public class JavaScriptModelEnumTest {
@Test(description = "convert a JavaScript model with an enum")
public void converterTest() {
final StringProperty enumProperty = new StringProperty();
enumProperty.setEnum(Arrays.asList("VALUE1", "VALUE2", "VALUE3"));
final ModelImpl model = new ModelImpl().property("name", enumProperty);
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.vars.size(), 1);
final CodegenProperty enumVar = cm.vars.get(0);
Assert.assertEquals(enumVar.baseName, "name");
Assert.assertEquals(enumVar.datatype, "String");
Assert.assertEquals(enumVar.datatypeWithEnum, "NameEnum");
Assert.assertEquals(enumVar.name, "name");
Assert.assertEquals(enumVar.defaultValue, null);
Assert.assertEquals(enumVar.baseType, "String");
Assert.assertTrue(enumVar.isEnum);
}
@Test(description = "not override identical parent enums")
public void overrideEnumTest() {
final StringProperty identicalEnumProperty = new StringProperty();
identicalEnumProperty.setEnum(Arrays.asList("VALUE1", "VALUE2", "VALUE3"));
final StringProperty subEnumProperty = new StringProperty();
subEnumProperty.setEnum(Arrays.asList("SUB1", "SUB2", "SUB3"));
// Add one enum property to the parent
final Map<String, Property> parentProperties = new HashMap<String, Property>();
parentProperties.put("sharedThing", identicalEnumProperty);
// Add TWO enums to the subType model; one of which is identical to the one in parent class
final Map<String, Property> subProperties = new HashMap<String, Property>();
subProperties.put("sharedThing", identicalEnumProperty);
subProperties.put("unsharedThing", identicalEnumProperty);
final ModelImpl parentModel = new ModelImpl();
parentModel.setProperties(parentProperties);
parentModel.name("parentModel");
final ModelImpl subModel = new ModelImpl();
subModel.setProperties(subProperties);
subModel.name("subModel");
final ComposedModel model = new ComposedModel()
.parent(new RefModel(parentModel.getName()))
.child(subModel)
.interfaces(new ArrayList<RefModel>());
final DefaultCodegen codegen = new JavaClientCodegen();
final Map<String, Model> allModels = new HashMap<String, Model>();
allModels.put(parentModel.getName(), parentModel);
allModels.put(subModel.getName(), subModel);
final CodegenModel cm = codegen.fromModel("sample", model, allModels);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.parent, "ParentModel");
Assert.assertTrue(cm.imports.contains("ParentModel"));
// Assert that only the unshared/uninherited enum remains
Assert.assertEquals(cm.vars.size(), 1);
final CodegenProperty enumVar = cm.vars.get(0);
Assert.assertEquals(enumVar.baseName, "unsharedThing");
Assert.assertEquals(enumVar.datatype, "String");
Assert.assertEquals(enumVar.datatypeWithEnum, "UnsharedThingEnum");
Assert.assertTrue(enumVar.isEnum);
}
}

View File

@ -0,0 +1,491 @@
package io.swagger.codegen.javascript;
import java.util.List;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import com.google.common.collect.Sets;
import io.swagger.codegen.CodegenModel;
import io.swagger.codegen.CodegenParameter;
import io.swagger.codegen.CodegenProperty;
import io.swagger.codegen.DefaultCodegen;
import io.swagger.codegen.languages.JavascriptClientCodegen;
import io.swagger.models.ArrayModel;
import io.swagger.models.Model;
import io.swagger.models.ModelImpl;
import io.swagger.models.parameters.QueryParameter;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.ByteArrayProperty;
import io.swagger.models.properties.DateTimeProperty;
import io.swagger.models.properties.IntegerProperty;
import io.swagger.models.properties.LongProperty;
import io.swagger.models.properties.MapProperty;
import io.swagger.models.properties.RefProperty;
import io.swagger.models.properties.StringProperty;
@SuppressWarnings("static-method")
public class JavaScriptModelTest {
@Test(description = "convert a simple java model")
public void simpleModelTest() {
final Model model = new ModelImpl()
.description("a sample model")
.property("id", new LongProperty())
.property("name", new StringProperty()
.example("Tony"))
.property("createdAt", new DateTimeProperty())
.required("id")
.required("name");
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 3);
final List<CodegenProperty> vars = cm.vars;
final CodegenProperty property1 = vars.get(0);
Assert.assertEquals(property1.baseName, "id");
Assert.assertEquals(property1.getter, "getId");
Assert.assertEquals(property1.setter, "setId");
Assert.assertEquals(property1.datatype, "Integer");
Assert.assertEquals(property1.name, "id");
Assert.assertEquals(property1.defaultValue, null);
Assert.assertEquals(property1.baseType, "Integer");
Assert.assertTrue(property1.hasMore);
Assert.assertTrue(property1.required);
Assert.assertTrue(property1.isNotContainer);
final CodegenProperty property2 = vars.get(1);
Assert.assertEquals(property2.baseName, "name");
Assert.assertEquals(property2.getter, "getName");
Assert.assertEquals(property2.setter, "setName");
Assert.assertEquals(property2.datatype, "String");
Assert.assertEquals(property2.name, "name");
Assert.assertEquals(property2.defaultValue, null);
Assert.assertEquals(property2.baseType, "String");
Assert.assertEquals(property2.example, "Tony");
Assert.assertTrue(property2.hasMore);
Assert.assertTrue(property2.required);
Assert.assertTrue(property2.isNotContainer);
final CodegenProperty property3 = vars.get(2);
Assert.assertEquals(property3.baseName, "createdAt");
Assert.assertEquals(property3.getter, "getCreatedAt");
Assert.assertEquals(property3.setter, "setCreatedAt");
Assert.assertEquals(property3.datatype, "Date");
Assert.assertEquals(property3.name, "createdAt");
Assert.assertEquals(property3.defaultValue, null);
Assert.assertEquals(property3.baseType, "Date");
Assert.assertNull(property3.hasMore);
Assert.assertNull(property3.required);
Assert.assertTrue(property3.isNotContainer);
}
@Test(description = "convert a model with list property")
public void listPropertyTest() {
final Model model = new ModelImpl()
.description("a sample model")
.property("id", new LongProperty())
.property("urls", new ArrayProperty()
.items(new StringProperty()))
.required("id");
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 2);
final CodegenProperty property = cm.vars.get(1);
Assert.assertEquals(property.baseName, "urls");
Assert.assertEquals(property.getter, "getUrls");
Assert.assertEquals(property.setter, "setUrls");
Assert.assertEquals(property.datatype, "[String]");
Assert.assertEquals(property.name, "urls");
// FIXME: should an array property have an empty array as its default value? What if the property is required?
Assert.assertEquals(property.defaultValue, /*"[]"*/null);
Assert.assertEquals(property.baseType, "Array");
Assert.assertEquals(property.containerType, "array");
Assert.assertNull(property.required);
Assert.assertTrue(property.isContainer);
}
@Test(description = "convert a model with a map property")
public void mapPropertyTest() {
final Model model = new ModelImpl()
.description("a sample model")
.property("translations", new MapProperty()
.additionalProperties(new StringProperty()))
.required("id");
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 1);
final CodegenProperty property = cm.vars.get(0);
Assert.assertEquals(property.baseName, "translations");
Assert.assertEquals(property.getter, "getTranslations");
Assert.assertEquals(property.setter, "setTranslations");
Assert.assertEquals(property.datatype, "{String: String}");
Assert.assertEquals(property.name, "translations");
// FIXME: should a map property have an empty object as its default value? What if the property is required?
Assert.assertEquals(property.defaultValue, /*"{}"*/null);
Assert.assertEquals(property.baseType, "Object");
Assert.assertEquals(property.containerType, "map");
Assert.assertNull(property.required);
Assert.assertTrue(property.isContainer);
}
@Test(description = "convert a model with a map with complex list property")
public void mapWithListPropertyTest() {
final Model model = new ModelImpl()
.description("a sample model")
.property("translations",
new MapProperty().additionalProperties(new ArrayProperty().items(new RefProperty("Pet"))))
.required("id");
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 1);
final CodegenProperty property = cm.vars.get(0);
Assert.assertEquals(property.baseName, "translations");
Assert.assertEquals(property.getter, "getTranslations");
Assert.assertEquals(property.setter, "setTranslations");
Assert.assertEquals(property.datatype, "{String: [Pet]}");
Assert.assertEquals(property.name, "translations");
// FIXME: should a map property have an empty object as its default value? What if the property is required?
Assert.assertEquals(property.defaultValue, /*"{}"*/null);
Assert.assertEquals(property.baseType, "Object");
Assert.assertEquals(property.containerType, "map");
Assert.assertNull(property.required);
Assert.assertTrue(property.isContainer);
}
@Test(description = "convert a model with a 2D list property")
public void list2DPropertyTest() {
final Model model = new ModelImpl().name("sample").property("list2D", new ArrayProperty().items(
new ArrayProperty().items(new RefProperty("Pet"))));
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.vars.size(), 1);
final CodegenProperty property = cm.vars.get(0);
Assert.assertEquals(property.baseName, "list2D");
Assert.assertEquals(property.getter, "getList2D");
Assert.assertEquals(property.setter, "setList2D");
Assert.assertEquals(property.datatype, "[[Pet]]");
Assert.assertEquals(property.name, "list2D");
// FIXME: should an array property have an empty array as its default value? What if the property is required?
Assert.assertEquals(property.defaultValue, /*"[]"*/null);
Assert.assertEquals(property.baseType, "Array");
Assert.assertEquals(property.containerType, "array");
Assert.assertNull(property.required);
Assert.assertTrue(property.isContainer);
}
@Test(description = "convert a model with complex properties")
public void complexPropertiesTest() {
final Model model = new ModelImpl().description("a sample model")
.property("children", new RefProperty("#/definitions/Children"));
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 1);
final CodegenProperty property = cm.vars.get(0);
Assert.assertEquals(property.baseName, "children");
Assert.assertEquals(property.getter, "getChildren");
Assert.assertEquals(property.setter, "setChildren");
Assert.assertEquals(property.datatype, "Children");
Assert.assertEquals(property.name, "children");
Assert.assertEquals(property.defaultValue, null);
Assert.assertEquals(property.baseType, "Children");
Assert.assertNull(property.required);
Assert.assertTrue(property.isNotContainer);
}
@Test(description = "convert a model with complex list property")
public void complexListPropertyTest() {
final Model model = new ModelImpl()
.description("a sample model")
.property("children", new ArrayProperty().items(new RefProperty("#/definitions/Children")));
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 1);
final CodegenProperty property = cm.vars.get(0);
Assert.assertEquals(property.baseName, "children");
Assert.assertEquals(property.complexType, "Children");
Assert.assertEquals(property.getter, "getChildren");
Assert.assertEquals(property.setter, "setChildren");
// FIXME: what should datatype be for a JavaScript array?
// Assert.assertEquals(property.datatype, "Array<Children>");
Assert.assertEquals(property.name, "children");
// FIXME: should an array property have an empty array as its default value? What if the property is required?
Assert.assertEquals(property.defaultValue, /*"[]"*/null);
Assert.assertEquals(property.baseType, "Array");
Assert.assertEquals(property.containerType, "array");
Assert.assertNull(property.required);
Assert.assertTrue(property.isContainer);
}
@Test(description = "convert a model with complex map property")
public void complexMapPropertyTest() {
final Model model = new ModelImpl()
.description("a sample model")
.property("children", new MapProperty().additionalProperties(new RefProperty("#/definitions/Children")));
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 1);
Assert.assertEquals(Sets.intersection(cm.imports, Sets.newHashSet("Children")).size(), 1);
final CodegenProperty property = cm.vars.get(0);
Assert.assertEquals(property.baseName, "children");
Assert.assertEquals(property.complexType, "Children");
Assert.assertEquals(property.getter, "getChildren");
Assert.assertEquals(property.setter, "setChildren");
// TODO: create a functional test to see whether map properties actually work.
Assert.assertEquals(property.datatype, /*"Object<String, Children>"*/"{String: Children}");
Assert.assertEquals(property.name, "children");
// FIXME: should a map property have an empty object as its default value? What if the property is required?
Assert.assertEquals(property.defaultValue, /*"{}"*/ null);
Assert.assertEquals(property.baseType, "Object");
Assert.assertEquals(property.containerType, "map");
Assert.assertNull(property.required);
Assert.assertTrue(property.isContainer);
Assert.assertNull(property.isNotContainer);
}
@Test(description = "convert an array model")
public void arrayModelTest() {
final Model model = new ArrayModel()
.description("an array model")
.items(new RefProperty("#/definitions/Children"));
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "an array model");
Assert.assertEquals(cm.vars.size(), 0);
Assert.assertEquals(cm.parent, "Array<Children>");
Assert.assertEquals(cm.imports.size(), 1);
Assert.assertEquals(Sets.intersection(cm.imports, Sets.newHashSet("Children")).size(), 1);
}
@Test(description = "convert a map model")
public void mapModelTest() {
final Model model = new ModelImpl()
.description("an map model")
.additionalProperties(new RefProperty("#/definitions/Children"));
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "an map model");
Assert.assertEquals(cm.vars.size(), 0);
Assert.assertEquals(cm.parent, "Object<String, Children>");
Assert.assertEquals(cm.imports.size(), 1);
Assert.assertEquals(Sets.intersection(cm.imports, Sets.newHashSet("Children")).size(), 1);
}
@Test(description = "convert a model with uppercase property names")
public void upperCaseNamesTest() {
final Model model = new ModelImpl()
.description("a model with uppercase property names")
.property("NAME", new StringProperty())
.required("NAME");
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.vars.size(), 1);
final CodegenProperty property = cm.vars.get(0);
Assert.assertEquals(property.baseName, "NAME");
Assert.assertEquals(property.getter, "getNAME");
Assert.assertEquals(property.setter, "setNAME");
Assert.assertEquals(property.datatype, "String");
Assert.assertEquals(property.name, "NAME");
Assert.assertEquals(property.defaultValue, null);
Assert.assertEquals(property.baseType, "String");
Assert.assertNull(property.hasMore);
Assert.assertTrue(property.required);
Assert.assertTrue(property.isNotContainer);
}
@Test(description = "convert a model with a 2nd char uppercase property names")
public void secondCharUpperCaseNamesTest() {
final Model model = new ModelImpl()
.description("a model with a 2nd char uppercase property names")
.property("pId", new StringProperty())
.required("pId");
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.vars.size(), 1);
final CodegenProperty property = cm.vars.get(0);
Assert.assertEquals(property.baseName, "pId");
Assert.assertEquals(property.getter, "getPId");
Assert.assertEquals(property.setter, "setPId");
Assert.assertEquals(property.datatype, "String");
Assert.assertEquals(property.name, "pId");
Assert.assertEquals(property.defaultValue, null);
Assert.assertEquals(property.baseType, "String");
Assert.assertNull(property.hasMore);
Assert.assertTrue(property.required);
Assert.assertTrue(property.isNotContainer);
}
@Test(description = "convert hyphens per issue 503")
public void hyphensTest() {
final Model model = new ModelImpl()
.description("a sample model")
.property("created-at", new DateTimeProperty());
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
final CodegenProperty property = cm.vars.get(0);
Assert.assertEquals(property.baseName, "created-at");
Assert.assertEquals(property.getter, "getCreatedAt");
Assert.assertEquals(property.setter, "setCreatedAt");
Assert.assertEquals(property.name, "createdAt");
}
@Test(description = "convert query[password] to queryPassword")
public void squareBracketsTest() {
final Model model = new ModelImpl()
.description("a sample model")
.property("query[password]", new StringProperty());
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
final CodegenProperty property = cm.vars.get(0);
Assert.assertEquals(property.baseName, "query[password]");
Assert.assertEquals(property.getter, "getQueryPassword");
Assert.assertEquals(property.setter, "setQueryPassword");
Assert.assertEquals(property.name, "queryPassword");
}
@Test(description = "properly escape names per 567")
public void escapeNamesTest() {
final Model model = new ModelImpl()
.description("a sample model")
.property("created-at", new DateTimeProperty());
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("with.dots", model);
Assert.assertEquals(cm.classname, "WithDots");
}
@Test(description = "convert a model with binary data")
public void binaryDataTest() {
final Model model = new ModelImpl()
.description("model with binary")
.property("inputBinaryData", new ByteArrayProperty());
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
final CodegenProperty property = cm.vars.get(0);
Assert.assertEquals(property.baseName, "inputBinaryData");
Assert.assertEquals(property.getter, "getInputBinaryData");
Assert.assertEquals(property.setter, "setInputBinaryData");
Assert.assertEquals(property.datatype, "String");
Assert.assertEquals(property.name, "inputBinaryData");
Assert.assertEquals(property.defaultValue, null);
Assert.assertEquals(property.baseType, "String");
Assert.assertNull(property.hasMore);
Assert.assertNull(property.required);
Assert.assertTrue(property.isNotContainer);
}
@Test(description = "translate an invalid param name")
public void invalidParamNameTest() {
final Model model = new ModelImpl()
.description("a model with a 2nd char uppercase property name")
.property("_", new StringProperty());
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel("sample", model);
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.vars.size(), 1);
final CodegenProperty property = cm.vars.get(0);
Assert.assertEquals(property.baseName, "_");
Assert.assertEquals(property.getter, "getU");
Assert.assertEquals(property.setter, "setU");
Assert.assertEquals(property.datatype, "String");
Assert.assertEquals(property.name, "u");
Assert.assertEquals(property.defaultValue, null);
Assert.assertEquals(property.baseType, "String");
Assert.assertNull(property.hasMore);
Assert.assertTrue(property.isNotContainer);
}
@Test(description = "convert a parameter")
public void convertParameterTest() {
final QueryParameter parameter = new QueryParameter()
.property(new IntegerProperty())
.name("limit")
.required(true);
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenParameter cm = codegen.fromParameter(parameter, null);
Assert.assertNull(cm.allowableValues);
}
@DataProvider(name = "modelNames")
public static Object[][] primeNumbers() {
return new Object[][] {
{"sample", "Sample"},
{"sample_name", "SampleName"},
{"sample__name", "SampleName"},
{"/sample", "Sample"},
{"\\sample", "Sample"},
{"sample.name", "SampleName"},
{"_sample", "Sample"},
{"Sample", "Sample"},
};
}
@Test(dataProvider = "modelNames", description = "avoid inner class")
public void modelNameTest(String name, String expectedName) {
final Model model = new ModelImpl();
final DefaultCodegen codegen = new JavascriptClientCodegen();
final CodegenModel cm = codegen.fromModel(name, model);
Assert.assertEquals(cm.name, name);
Assert.assertEquals(cm.classname, expectedName);
}
}

View File

@ -0,0 +1,88 @@
package io.swagger.codegen.options;
import io.swagger.codegen.CodegenConstants;
import io.swagger.codegen.options.OptionsProvider;
import io.swagger.codegen.languages.JavascriptClientCodegen;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
public class JavaScriptOptionsProvider implements OptionsProvider {
public static final String ARTIFACT_ID_VALUE = "swagger-javascript-client-test";
public static final String MODEL_PACKAGE_VALUE = "model";
public static final String API_PACKAGE_VALUE = "api";
// public static final String INVOKER_PACKAGE_VALUE = "js";
public static final String SORT_PARAMS_VALUE = "false";
public static final String GROUP_ID_VALUE = "io.swagger.test";
public static final String ARTIFACT_VERSION_VALUE = "1.0.0-SNAPSHOT";
public static final String SOURCE_FOLDER_VALUE = "src/main/javascript";
public static final String LOCAL_PREFIX_VALUE = "_";
// public static final String SERIALIZABLE_MODEL_VALUE = "false";
public static final String ENSURE_UNIQUE_PARAMS_VALUE = "true";
public static final String PROJECT_NAME_VALUE = "JavaScript Client Test";
public static final String MODULE_NAME_VALUE = "JavaScriptClient";
public static final String PROJECT_DESCRIPTION_VALUE = "Tests JavaScript code generator options";
public static final String PROJECT_VERSION_VALUE = "1.0.0";
public static final String PROJECT_LICENSE_NAME_VALUE = "Apache";
public static final String USE_PROMISES_VALUE = "true";
public static final String USE_INHERITANCE_VALUE = "false";
public static final String EMIT_MODEL_METHODS_VALUE = "true";
public static final String EMIT_JS_DOC_VALUE = "false";
private ImmutableMap<String, String> options;
/**
* Create an options provider with the default options.
*/
public JavaScriptOptionsProvider() {
// Commented generic options not yet supported by JavaScript codegen.
options = new ImmutableMap.Builder<String, String>()
.put(CodegenConstants.MODEL_PACKAGE, MODEL_PACKAGE_VALUE)
.put(CodegenConstants.API_PACKAGE, API_PACKAGE_VALUE)
.put(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, SORT_PARAMS_VALUE)
.put(CodegenConstants.ENSURE_UNIQUE_PARAMS, ENSURE_UNIQUE_PARAMS_VALUE)
// .put(CodegenConstants.INVOKER_PACKAGE, INVOKER_PACKAGE_VALUE)
// .put(CodegenConstants.GROUP_ID, GROUP_ID_VALUE)
// .put(CodegenConstants.ARTIFACT_ID, ARTIFACT_ID_VALUE)
// .put(CodegenConstants.ARTIFACT_VERSION, ARTIFACT_VERSION_VALUE)
.put(CodegenConstants.SOURCE_FOLDER, SOURCE_FOLDER_VALUE)
.put(CodegenConstants.LOCAL_VARIABLE_PREFIX, LOCAL_PREFIX_VALUE)
// .put(CodegenConstants.SERIALIZE_BIG_DECIMAL_AS_STRING, "true")
.put(JavascriptClientCodegen.PROJECT_NAME, PROJECT_NAME_VALUE)
.put(JavascriptClientCodegen.MODULE_NAME, MODULE_NAME_VALUE)
.put(JavascriptClientCodegen.PROJECT_DESCRIPTION, PROJECT_DESCRIPTION_VALUE)
.put(JavascriptClientCodegen.PROJECT_VERSION, PROJECT_VERSION_VALUE)
.put(JavascriptClientCodegen.PROJECT_LICENSE_NAME, PROJECT_LICENSE_NAME_VALUE)
.put(JavascriptClientCodegen.USE_PROMISES, USE_PROMISES_VALUE)
.put(JavascriptClientCodegen.USE_INHERITANCE, USE_INHERITANCE_VALUE)
.put(JavascriptClientCodegen.EMIT_MODEL_METHODS, EMIT_MODEL_METHODS_VALUE)
.put(JavascriptClientCodegen.EMIT_JS_DOC, EMIT_JS_DOC_VALUE)
.build();
}
/**
* Use the default options, but override the ones found in additionalOptions.
*/
public JavaScriptOptionsProvider(Map<String, String> additionalOptions) {
options = new ImmutableMap.Builder<String, String>()
.putAll(options)
.putAll(additionalOptions)
.build();
}
@Override
public Map<String, String> createOptions() {
return options;
}
@Override
public boolean isServer() {
return false;
}
@Override
public String getLanguage() {
return "javascript";
}
}