diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenModel.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenModel.java index 6fd620fc7f74..a7866e5675a5 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenModel.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenModel.java @@ -6,20 +6,35 @@ import java.util.*; public class CodegenModel { public String parent, parentSchema; + public List interfaces; + + // References to parent and interface CodegenModels. Only set when code generator supports inheritance. + public CodegenModel parentModel; + public List 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 vars = new ArrayList(); + public List allVars; public List allowableValues; - // list of all required parameters - public Set mandatory = new HashSet(); - + // Sorted sets of required parameters. + public Set mandatory = new TreeSet(); + public Set allMandatory; + public Set imports = new TreeSet(); public Boolean hasVars, emptyVars, hasMoreModels, hasEnums, isEnum; public ExternalDocs externalDocs; public Map 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; + } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java index 86bb99598ec9..300bf4192fc7 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java @@ -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 postProcessAllModels(Map objs) { + if (supportsInheritance) { + // Index all CodegenModels by name. + Map allModels = new HashMap(); + for (Entry entry : objs.entrySet()) { + String modelName = entry.getKey(); + Map inner = (Map) entry.getValue(); + List> models = (List>) inner.get("models"); + for (Map 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(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 properties = new HashMap(); + Map properties = new LinkedHashMap(); List required = new ArrayList(); + Map allProperties; + List allRequired; + if (supportsInheritance) { + allProperties = new LinkedHashMap(); + allRequired = new ArrayList(); + m.allVars = new ArrayList(); + } 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(); 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 properties, List required, Model model, + Map 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)) { @@ -2157,45 +2216,65 @@ public class DefaultCodegen { } } - private void addVars(CodegenModel m, Map properties, Collection required) { - if (properties != null && properties.size() > 0) { + private void addVars(CodegenModel m, Map properties, List required) { + addVars(m, properties, required, null, null); + } + + private void addVars(CodegenModel m, Map properties, List required, + Map allProperties, List allRequired) { + + if (properties != null && !properties.isEmpty()) { m.hasVars = true; m.hasEnums = false; - final int totalCount = properties.size(); - final Set mandatory = required == null ? Collections.emptySet() : new HashSet(required); - int count = 0; - for (Map.Entry 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 mandatory = required == null ? Collections. emptySet() + : new TreeSet(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 allMandatory = allRequired == null ? Collections. emptySet() + : new TreeSet(allRequired); + addVars(m, m.allVars, allProperties, allMandatory); + m.allMandatory = allMandatory; + } + } + + private void addVars(CodegenModel m, List vars, Map properties, Set mandatory) { + final int totalCount = properties.size(); + int count = 0; + for (Map.Entry 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); + } + } } /** diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavascriptClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavascriptClientCodegen.java index 6dad5b35db0a..d9c93e4f69ea 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavascriptClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavascriptClientCodegen.java @@ -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(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(); - 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 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."; + } + 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."; + } + } + 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 postProcessOperations(Map 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 ops = (List) operations.get("operation"); for (CodegenOperation operation : ops) { - List argList = new ArrayList(); + List argList = new ArrayList(); 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 postProcessModels(Map objs) { List models = (List) objs.get("models"); for (Object _mo : models) { Map mo = (Map) _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 required = new ArrayList(); + List allRequired = supportsInheritance ? new ArrayList() : required; + cm.vendorExtensions.put("x-required", required); + cm.vendorExtensions.put("x-all-required", allRequired); + for (CodegenProperty var : cm.vars) { Map 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) { diff --git a/modules/swagger-codegen/src/main/resources/Javascript/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Javascript/ApiClient.mustache index b8311bd9709d..25f5474523f0 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/ApiClient.mustache @@ -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.} + */ +{{/emitJSDoc}}{{=< >=}} this.authentications = {<#authMethods><#isBasic> '': {type: 'basic'}<#isApiKey> - '': {type: 'apiKey', in: <#isKeyInHeader>'header'<^isKeyInHeader>'query', name: ''}<#isOAuth> + '': {type: 'apiKey', 'in': <#isKeyInHeader>'header'<^isKeyInHeader>'query', name: ''}<#isOAuth> '': {type: 'oauth2'}<#hasMore>, }; <={{ }}=> - /** +{{#emitJSDoc}} /** * The default HTTP headers to be included for all API calls. + * @type {Array.} + * @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 param. + */ +{{/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.
+ * JSON content type examples:
+ *
    + *
  • application/json
  • + *
  • application/json; charset=UTF8
  • + *
  • APPLICATION/JSON
  • + *
+ * @param {String} contentType The MIME content type to check. + * @returns {Boolean} true if contentType represents JSON, otherwise false. */ - 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.} 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} true if param 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: + *
    + *
  • remove nils
  • + *
  • keep files and arrays
  • + *
  • format to string with `paramToString` for other cases
  • + *
+ * @param {Object.} params The parameters as object properties. + * @returns {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: csv + * @const + */ + CSV: ',', + /** + * Space-separated values. Value: ssv + * @const + */ + SSV: ' ', + /** + * Tab-separated values. Value: tsv + * @const + */ + TSV: '\t', + /** + * Pipe(|)-separated values. Value: pipes + * @const + */ + PIPES: '|', + /** + * Native array. Value: multi + * @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 + * param as is if collectionFormat is multi. + */ +{{/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 superagent() call. + * @param {Array.} 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.|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 data 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.} pathParams A map of path parameters and their values. + * @param {Object.} queryParams A map of query parameters and their values. + * @param {Object.} headerParams A map of header parameters and their values. + * @param {Object.} formParams A map of form parameters and their values. + * @param {Object} bodyParam The value to pass as the request body. + * @param {Array.} authNames An array of authentication type names. + * @param {Array.} contentTypes An array of request MIME types. + * @param {Array.} 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.|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 data 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; })); diff --git a/modules/swagger-codegen/src/main/resources/Javascript/api.mustache b/modules/swagger-codegen/src/main/resources/Javascript/api.mustache index 79a5fd2a6740..32a68f3b67fb 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/api.mustache @@ -1,10 +1,10 @@ {{=< >=}}(function(root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. - define(['../ApiClient'<#imports>, '../model/'], factory); + define(['../ApiClient'<#imports>, '../<#modelPackage>/'], 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/')); + module.exports = factory(require('../ApiClient')<#imports>, require('../<#modelPackage>/')); } else { // Browser globals (root is window) if (!root.) { @@ -15,28 +15,50 @@ }(this, function(ApiClient<#imports>, ) { 'use strict'; - var = function (apiClient) { - this.apiClient = apiClient || ApiClient.default; +<#emitJSDoc> /** + * service. + * @module <#apiPackage>/ + * @version + */ - var self = this; - <#operations> - <#operation> + /** + * Constructs a new . <#description> + * + * @alias module:<#apiPackage>/ + * @class + * @param {module:ApiClient} apiClient Optional API client implementation to use, defaulting to {@link module:ApiClient#instance} + * if unspecified. + */ + var exports = function(apiClient) { + this.apiClient = apiClient || ApiClient.instance; + +<#operations><#operation><#emitJSDoc><^usePromises> /** - * - * <#allParams> - * @param {} <#required><^required>opts[''] <^usePromises> - * @param {function} callback the callback function, accepting three arguments: error, data, response<#returnType> - * data is of type: <&returnType> + * Callback function to receive the result of the operation. + * @callback module:<#apiPackage>/~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>data This operation does not return a value. + * @param {String} response The complete HTTP response. + */ + + /**<#summary> + * <#notes> + * <#allParams><#required> + * @param <&vendorExtensions.x-jsdoc-type> <#hasOptionalParams> + * @param {Object} opts Optional parameters<#allParams><^required> + * @param <&vendorExtensions.x-jsdoc-type> opts. <^usePromises> + * @param {module:<#apiPackage>/~Callback} callback The callback function, accepting three arguments: error, data, response<#returnType> + * data is of type: <&vendorExtensions.x-jsdoc-type> */ - self. = function() {<#hasOptionalParams> + this. = function() {<#hasOptionalParams> opts = opts || {}; var postBody = <#bodyParam><#required><^required>opts['']<^bodyParam>null; - <#allParams><#required> +<#allParams><#required> // verify the required parameter '' is set - if ( == null) { + if ( == undefined || == null) { throw "Missing the required parameter '' when calling "; } - + var pathParams = {<#pathParams> '': <#required><^required>opts['']<#hasMore>, @@ -61,11 +83,8 @@ pathParams, queryParams, headerParams, formParams, postBody, authNames, contentTypes, accepts, returnType<^usePromises>, callback ); - } - - - }; + }; - return ; + return exports; }));<={{ }}=> diff --git a/modules/swagger-codegen/src/main/resources/Javascript/enumClass.mustache b/modules/swagger-codegen/src/main/resources/Javascript/enumClass.mustache index 5acd5504bb59..d245084042ae 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/enumClass.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/enumClass.mustache @@ -1,10 +1,13 @@ -var {{datatypeWithEnum}} = { -{{#allowableValues}}{{#enumVars}} - /** - * @const - */ - {{name}}: "{{value}}"{{^-last}}, - {{/-last}}{{/enumVars}}{{/allowableValues}} - }; - - {{classname}}.{{datatypeWithEnum}} = {{datatypeWithEnum}}; +{{#emitJSDoc}} /** + * Allowed values for the {{baseName}} property. + * @enum {{=<% %>=}}{<%datatype%>}<%={{ }}=%> + * @readonly + */{{/emitJSDoc}} + exports.{{datatypeWithEnum}} = { {{#allowableValues}}{{#enumVars}} +{{#emitJSDoc}} /** + * value: {{value}} + * @const + */ +{{/emitJSDoc}} {{name}}: "{{value}}"{{^-last}}, + {{/-last}}{{/enumVars}}{{/allowableValues}} + }; \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/Javascript/index.mustache b/modules/swagger-codegen/src/main/resources/Javascript/index.mustache index 760c8a3cd73e..b48956afbb45 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/index.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/index.mustache @@ -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}}.
{{/projectDescription}} + * The index module provides access to constructors for all the classes which comprise the public API. + *

+ * An AMD (recommended!) or CommonJS application will generally do something equivalent to the following: + *

+   * 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.
+   * ...
+   * 
+ * *NOTE: For a top-level AMD script, use require(['./index'], function(){...}) and put the application logic within the + * callback function. + *

+ *

+ * A non-AMD browser application (discouraged) might do something like this: + *

+   * 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.
+   * ...
+   * 
+ *

+ * @module index + * @version {{projectVersion}} + */{{/emitJSDoc}} +{{=< >=}} var exports = {<#emitJSDoc> + /** + * The ApiClient constructor. + * @property {module:ApiClient} + */ + ApiClient: ApiClient<#models>,<#emitJSDoc> + /** + * The model constructor. + * @property {module:<#modelPackage>/} + */ + : <#apiInfo><#apis>,<#emitJSDoc> + /** + * The service constructor. + * @property {module:<#apiPackage>/} + */ + : }; + + return exports;<={{ }}=> })); diff --git a/modules/swagger-codegen/src/main/resources/Javascript/model.mustache b/modules/swagger-codegen/src/main/resources/Javascript/model.mustache index 818583686686..c8849223fc4b 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/model.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/model.mustache @@ -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 {{classname}}.{{#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 {{classname}} from a plain JavaScript object, optionally creating a new instance. + * Copies all relevant properties from data to obj if supplied or a new instance if not. + * @param {Object} data The plain JavaScript object bearing properties of interest. + * @param {{=< >=}}{module:<#modelPackage>/}<={{ }}=> obj Optional instance to populate. + * @return {{=< >=}}{module:<#modelPackage>/}<={{ }}=> The populated {{classname}} 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}}})); diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/javascript/JavaScriptClientOptionsTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/javascript/JavaScriptClientOptionsTest.java new file mode 100644 index 000000000000..927d4d7755b8 --- /dev/null +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/javascript/JavaScriptClientOptionsTest.java @@ -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; + }}; + } +} diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/javascript/JavaScriptInheritanceTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/javascript/JavaScriptInheritanceTest.java new file mode 100644 index 000000000000..94285e468881 --- /dev/null +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/javascript/JavaScriptInheritanceTest.java @@ -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 allDefinitions = new HashMap(); + 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 allDefinitions = new HashMap(); + 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")); + } +} diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/javascript/JavaScriptModelEnumTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/javascript/JavaScriptModelEnumTest.java new file mode 100644 index 000000000000..5e2306d73fbd --- /dev/null +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/javascript/JavaScriptModelEnumTest.java @@ -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 parentProperties = new HashMap(); + parentProperties.put("sharedThing", identicalEnumProperty); + + // Add TWO enums to the subType model; one of which is identical to the one in parent class + final Map subProperties = new HashMap(); + 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()); + + final DefaultCodegen codegen = new JavaClientCodegen(); + final Map allModels = new HashMap(); + 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); + } +} diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/javascript/JavaScriptModelTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/javascript/JavaScriptModelTest.java new file mode 100644 index 000000000000..bb7afb942b7d --- /dev/null +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/javascript/JavaScriptModelTest.java @@ -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 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"); + 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}"); + 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"); + 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"); + 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); + } +} diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/JavaScriptOptionsProvider.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/JavaScriptOptionsProvider.java new file mode 100644 index 000000000000..9b0f06a10312 --- /dev/null +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/JavaScriptOptionsProvider.java @@ -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 options; + + /** + * Create an options provider with the default options. + */ + public JavaScriptOptionsProvider() { + // Commented generic options not yet supported by JavaScript codegen. + options = new ImmutableMap.Builder() + .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 additionalOptions) { + options = new ImmutableMap.Builder() + .putAll(options) + .putAll(additionalOptions) + .build(); + } + + @Override + public Map createOptions() { + return options; + } + + @Override + public boolean isServer() { + return false; + } + + @Override + public String getLanguage() { + return "javascript"; + } +}