diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 970257aba6a..023840bf8f2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,6 +42,7 @@ Code change should conform to the programming style guide of the respective lang - C++ (Tizen): https://wiki.tizen.org/Native_Platform_Coding_Idiom_and_Style_Guide#C.2B.2B_Coding_Style - Clojure: https://github.com/bbatsov/clojure-style-guide - Elixir: https://github.com/christopheradams/elixir_style_guide +- Eiffel: https://www.eiffel.org/doc/eiffel/Coding%20Standards - Erlang: https://github.com/inaka/erlang_guidelines - Haskell: https://github.com/tibbe/haskell-style-guide/blob/master/haskell-style.md - Java: https://google.github.io/styleguide/javaguide.html diff --git a/bin/eiffel-petstore.sh b/bin/eiffel-petstore.sh new file mode 100644 index 00000000000..859e45a53bc --- /dev/null +++ b/bin/eiffel-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +while [ -h "$SCRIPT" ] ; do + ls=`ls -ld "$SCRIPT"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + SCRIPT="$link" + else + SCRIPT=`dirname "$SCRIPT"`/"$link" + fi +done + +if [ ! -d "${APP_DIR}" ]; then + APP_DIR=`dirname "$SCRIPT"`/.. + APP_DIR=`cd "${APP_DIR}"; pwd` +fi + +executable="./modules/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn clean package +fi + +# if you've executed sbt assembly previously it will use that instead. +export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" +ags="$@ generate -t modules/swagger-codegen/src/main/resources/eiffel -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l eiffel -o samples/client/petstore/eiffel/" + +java $JAVA_OPTS -jar $executable $ags \ No newline at end of file diff --git a/bin/windows/eiffel-petstore.bat b/bin/windows/eiffel-petstore.bat new file mode 100644 index 00000000000..9028359e8c3 --- /dev/null +++ b/bin/windows/eiffel-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -DloggerPath=conf/log4j.properties +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l eiffel -o samples\client\petstore\eiffel + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractEiffelCogegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractEiffelCogegen.java new file mode 100644 index 00000000000..c6beb7f3a0c --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractEiffelCogegen.java @@ -0,0 +1,507 @@ +package io.swagger.codegen.languages; + +import static com.google.common.base.Strings.isNullOrEmpty; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + +import io.swagger.codegen.CliOption; +import io.swagger.codegen.CodegenConfig; +import io.swagger.codegen.CodegenConstants; +import io.swagger.codegen.CodegenModel; +import io.swagger.codegen.CodegenOperation; +import io.swagger.codegen.CodegenParameter; +import io.swagger.codegen.CodegenProperty; +import io.swagger.codegen.DefaultCodegen; +import io.swagger.codegen.utils.ModelUtils; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.Property; + +public abstract class AbstractEiffelCogegen extends DefaultCodegen implements CodegenConfig { + + private final Set parentModels = new HashSet<>(); + private final Multimap childrenByParent = ArrayListMultimap.create(); + + public AbstractEiffelCogegen(){ + super(); + setReservedWordsLowerCase(Arrays.asList( + // language reserved words + "across", "agent", "alias", "all", "and", "as", "assign", "attribute", "check", "class", "convert", + "create", "Current", "debug", "deferred", "do", "else", "elseif", "end", "ensure", "expanded", "export", + "external", "False", "feature", "from", "frozen", "if", "implies", "inherit", "inspect", "invariant", + "like", "local", "loop", "not", "note", "obsolete", "old", "once", "only", "or", "Precursor", + "redefine", "rename", "require", "rescue", "Result", "retry", "select", "separate", "then", "True", + "TUPLE", "undefine", "until", "variant", "Void", "when", "xor")); + + defaultIncludes = new HashSet(Arrays.asList("map", "array")); + + languageSpecificPrimitives = new HashSet( + Arrays.asList("BOOLEAN", "INTEGER_8", "INTEGER_16", "INTEGER_32", "INTEGER_64", "NATURAL_8", + "NATURAL_16", "NATURAL_32", "NATURAL_64", "REAL_32", "REAL_64")); + + instantiationTypes.clear(); + + typeMapping.clear(); + typeMapping.put("integer", "INTEGER_32"); + typeMapping.put("long", "INTEGER_64"); + typeMapping.put("number", "REAL_32"); + typeMapping.put("float", "REAL_32"); + typeMapping.put("double", "REAL_64"); + typeMapping.put("boolean", "BOOLEAN"); + typeMapping.put("string", "STRING_32"); + typeMapping.put("UUID", "UUID"); // + typeMapping.put("date", "DATE"); + typeMapping.put("DateTime", "DATE_TIME"); + typeMapping.put("date-time", "DATE_TIME"); + typeMapping.put("password", "STRING"); + typeMapping.put("File", "FILE"); + typeMapping.put("file", "FILE"); + typeMapping.put("binary", "STRING_32"); + typeMapping.put("ByteArray", "ARRAY [NATURAL_8]"); + typeMapping.put("object", "ANY"); + typeMapping.put("map", "STRING_TABLE"); + typeMapping.put("array", "LIST"); + typeMapping.put("list", "LIST"); + + instantiationTypes.put("array", "ARRAYED_LIST"); + instantiationTypes.put("list", "ARRAYED_LIST"); + instantiationTypes.put("map", "STRING_TABLE"); + + + cliOptions.clear(); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Eiffel Cluster name (convention: lowercase).") + .defaultValue("swagger")); + cliOptions + .add(new CliOption(CodegenConstants.PACKAGE_VERSION, "Eiffel package version.").defaultValue("1.0.0")); + cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, + "hides the timestamp when files were generated").defaultValue(Boolean.TRUE.toString())); + } + + @Override + public String escapeReservedWord(String name) { + // Can't start with an underscore, as our fields need to start with an + // UppercaseLetter so that Go treats them as public/visible. + + // Options? + // - MyName + // - AName + // - TheName + // - XName + // - X_Name + // ... or maybe a suffix? + // - Name_ ... think this will work. + if (this.reservedWordsMappings().containsKey(name)) { + return this.reservedWordsMappings().get(name); + } + if (name.matches("^\\d.*")) {// prepend var_ + return "var_" + name; + } + return "var_" + name; + } + + @Override + public String toVarName(String name) { + // replace - with _ e.g. created-at => created_at + name = sanitizeName(name.replaceAll("-", "_")); + + // if it's all uppper case, do nothing + if (name.matches("^[A-Z_]*$")) { + return name; + } + + // pet_id + // petId => pet_id + name = unCamelize(name); + + if (name.startsWith("_")){ + name = "var" + name; + } + + // for reserved word + if (isReservedWord(name)) { + name = escapeReservedWord(name); + } + + // for reserved word or word starting with number, append + if (name.matches("^\\d.*")) { + name = escapeReservedWord(name); + } + + return name; + } + + @Override + public String toParamName(String name) { + // params should be lowercase. E.g. "person: PERSON" + return toVarName(name).toLowerCase(); + } + + @Override + public String toModelName(String name) { + // phone_number => PHONE_NUMBER + return toModelFilename(name).toUpperCase(); + } + + @Override + public String toModelFilename(String name) { + if (!StringUtils.isEmpty(modelNamePrefix)) { + name = modelNamePrefix + "_" + name; + } + + if (!StringUtils.isEmpty(modelNameSuffix)) { + name = name + "_" + modelNameSuffix; + } + + name = sanitizeName(name); + + // model name cannot use reserved keyword, e.g. return + if (isReservedWord(name)) { + LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + ("model_" + name)); + name = "model_" + name; // e.g. return => ModelReturn (after + // camelize) + } + + // model name starts with number + if (name.matches("^\\d.*")) { + LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + + ("model_" + name)); + name = "model_" + name; // e.g. 200Response => Model200Response + // (after camelize) + } + + return underscore(name); + } + + @Override + public String toApiFilename(String name) { + // replace - with _ e.g. created-at => created_at + name = name.replaceAll("-", "_"); // FIXME: a parameter should not be + // assigned. Also declare the + // methods parameters as 'final'. + + // e.g. PetApi.go => pet_api.go + return underscore(name) + "_api"; + } + + @Override + public String toApiTestFilename(String name) { + return toApiName(name).toLowerCase() + "_test"; + } + + @Override + public String toApiName(String name) { + if (name.length() == 0) { + return "DEFAULT_API"; + } + return name.toUpperCase() + "_API"; + } + + /** + * Overrides postProcessParameter to add a vendor extension + * "x-exportParamName". This is useful when paramName starts with a + * lowercase letter, but we need that param to be exportable (starts with an + * Uppercase letter). + * + * @param parameter + * CodegenParameter object to be processed. + */ + @Override + public void postProcessParameter(CodegenParameter parameter) { + + // Give the base class a chance to process + super.postProcessParameter(parameter); + + char firstChar = parameter.paramName.charAt(0); + + if (Character.isUpperCase(firstChar)) { + // First char is already uppercase, just use paramName. + parameter.vendorExtensions.put("x-exportParamName", parameter.paramName); + + } + + // It's a lowercase first char, let's convert it to uppercase + StringBuilder sb = new StringBuilder(parameter.paramName); + sb.setCharAt(0, Character.toUpperCase(firstChar)); + parameter.vendorExtensions.put("x-exportParamName", sb.toString()); + } + + @Override + public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { + if (!isNullOrEmpty(model.parent)) { + parentModels.add(model.parent); + if (!childrenByParent.containsEntry(model.parent, model)) { + childrenByParent.put(model.parent, model); + } + } + if (!isNullOrEmpty(model.parentSchema)) { + model.parentSchema = model.parentSchema.toLowerCase(); + } + } + + @Override + public String toModelDocFilename(String name) { + return toModelName(name); + } + + @Override + public String toApiDocFilename(String name) { + return toApiName(name); + } + + @Override + public String getTypeDeclaration(Property p) { + if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + return "LIST [" + getTypeDeclaration(inner) + "]"; + } else if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + + return getSwaggerType(p) + "[" + getTypeDeclaration(inner) + "]"; + } + // return super.getTypeDeclaration(p); + + // Not using the supertype invocation, because we want to UpperCamelize + // the type. + String swaggerType = getSwaggerType(p); + if (typeMapping.containsKey(swaggerType)) { + return typeMapping.get(swaggerType); + } + + if (typeMapping.containsValue(swaggerType)) { + return swaggerType; + } + + if (languageSpecificPrimitives.contains(swaggerType)) { + return swaggerType; + } + + return toModelName(swaggerType); + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + String type = null; + if (typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + if (languageSpecificPrimitives.contains(type)) + return (type); + } else + type = swaggerType; + return type; + } + + @Override + public String toOperationId(String operationId) { + String sanitizedOperationId = sanitizeName(operationId); + + // method name cannot use reserved keyword, e.g. return + if (isReservedWord(sanitizedOperationId)) { + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + + camelize("call_" + operationId)); + sanitizedOperationId = "call_" + sanitizedOperationId; + } + // method name from updateSomething to update_Something. + sanitizedOperationId = unCamelize(sanitizedOperationId); + + return toEiffelFeatureStyle(sanitizedOperationId); + } + + @Override + public Map postProcessOperations(Map objs) { + @SuppressWarnings("unchecked") + Map objectMap = (Map) objs.get("operations"); + @SuppressWarnings("unchecked") + List operations = (List) objectMap.get("operation"); + for (CodegenOperation operation : operations) { + // http method verb conversion (e.g. PUT => Put) + + operation.httpMethod = camelize(operation.httpMethod.toLowerCase()); + } + + // remove model imports to avoid error + List> imports = (List>) objs.get("imports"); + if (imports == null) + return objs; + + Iterator> iterator = imports.iterator(); + while (iterator.hasNext()) { + String _import = iterator.next().get("import"); + if (_import.startsWith(apiPackage())) + iterator.remove(); + } + // if the return type is not primitive, import encoding/json + for (CodegenOperation operation : operations) { + if (operation.returnBaseType != null && needToImport(operation.returnBaseType)) { + imports.add(createMapping("import", "encoding/json")); + break; // just need to import once + } + } + + // this will only import "fmt" if there are items in pathParams + for (CodegenOperation operation : operations) { + if (operation.pathParams != null && operation.pathParams.size() > 0) { + imports.add(createMapping("import", "fmt")); + break; // just need to import once + } + } + + // recursively add import for mapping one type to multiple imports + List> recursiveImports = (List>) objs.get("imports"); + if (recursiveImports == null) + return objs; + + ListIterator> listIterator = imports.listIterator(); + while (listIterator.hasNext()) { + String _import = listIterator.next().get("import"); + // if the import package happens to be found in the importMapping + // (key) + // add the corresponding import package to the list + if (importMapping.containsKey(_import)) { + listIterator.add(createMapping("import", importMapping.get(_import))); + } + } + + return objs; + } + + @Override + public Map postProcessModels(Map objs) { + // remove model imports to avoid error + List> imports = (List>) objs.get("imports"); + final String prefix = modelPackage(); + Iterator> iterator = imports.iterator(); + while (iterator.hasNext()) { + String _import = iterator.next().get("import"); + if (_import.startsWith(prefix)) + iterator.remove(); + } + + // recursively add import for mapping one type to multiple imports + List> recursiveImports = (List>) objs.get("imports"); + if (recursiveImports == null) + return objs; + + ListIterator> listIterator = imports.listIterator(); + while (listIterator.hasNext()) { + String _import = listIterator.next().get("import"); + // if the import package happens to be found in the importMapping + // (key) + // add the corresponding import package to the list + if (importMapping.containsKey(_import)) { + listIterator.add(createMapping("import", importMapping.get(_import))); + } + } + + return objs; + } + + @Override + public Map postProcessAllModels(final Map models) { + + final Map processed = super.postProcessAllModels(models); + postProcessParentModels(models); + return processed; + } + + private void postProcessParentModels(final Map models) { + for (final String parent : parentModels) { + final CodegenModel parentModel = ModelUtils.getModelByName(parent, models); + final Collection childrenModels = childrenByParent.get(parent); + for (final CodegenModel child : childrenModels) { + processParentPropertiesInChildModel(parentModel, child); + } + } + } + + /** + * Sets the child property's isInherited flag to true if it is an inherited + * property + */ + private void processParentPropertiesInChildModel(final CodegenModel parent, final CodegenModel child) { + final Map childPropertiesByName = new HashMap<>(child.vars.size()); + for (final CodegenProperty childProperty : child.vars) { + childPropertiesByName.put(childProperty.name, childProperty); + } + for (final CodegenProperty parentProperty : parent.vars) { + final CodegenProperty duplicatedByParent = childPropertiesByName.get(parentProperty.name); + if (duplicatedByParent != null) { + duplicatedByParent.isInherited = true; + } + } + } + + @Override + protected boolean needToImport(String type) { + return !defaultIncludes.contains(type) && !languageSpecificPrimitives.contains(type); + } + + @Override + public String escapeQuotationMark(String input) { + // remove " to avoid code injection + return input.replace("\"", ""); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("*/", "*_/").replace("/*", "/_*"); + } + + public Map createMapping(String key, String value) { + Map customImport = new HashMap(); + customImport.put(key, value); + + return customImport; + } + + @Override + public String toInstantiationType(Property p) { + if (p instanceof MapProperty) { + MapProperty ap = (MapProperty) p; + Property additionalProperties2 = ap.getAdditionalProperties(); + String type = additionalProperties2.getType(); + if (null == type) { + LOGGER.error("No Type defined for Additional Property " + additionalProperties2 + "\n" // + + "\tIn Property: " + p); + } + String inner = toModelName(getSwaggerType(additionalProperties2)); + return instantiationTypes.get("map") + " [" + inner + "]"; + } else if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + String inner = toModelName(getSwaggerType(ap.getItems())); + return instantiationTypes.get("array") + " [" + inner + "]"; + } else { + return null; + } + } + + public String unCamelize(String name) { + return name.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase(); + } + + public String toEiffelFeatureStyle(String operationId) { + if (operationId.startsWith("get_")) { + return operationId.substring(4, operationId.length()); + } else { + return operationId; + } + + } + +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/EiffelClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/EiffelClientCodegen.java index ba697be3c2c..55c15e27fe2 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/EiffelClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/EiffelClientCodegen.java @@ -1,42 +1,16 @@ package io.swagger.codegen.languages; -import static com.google.common.base.Strings.isNullOrEmpty; - import java.io.File; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Set; import java.util.UUID; -import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; - -import io.swagger.codegen.CliOption; -import io.swagger.codegen.CodegenConfig; import io.swagger.codegen.CodegenConstants; -import io.swagger.codegen.CodegenModel; -import io.swagger.codegen.CodegenOperation; -import io.swagger.codegen.CodegenParameter; -import io.swagger.codegen.CodegenProperty; import io.swagger.codegen.CodegenType; -import io.swagger.codegen.DefaultCodegen; import io.swagger.codegen.SupportingFile; -import io.swagger.codegen.utils.ModelUtils; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.Property; -public class EiffelClientCodegen extends DefaultCodegen implements CodegenConfig { +public class EiffelClientCodegen extends AbstractEiffelCogegen { static Logger LOGGER = LoggerFactory.getLogger(EiffelClientCodegen.class); protected String libraryTarget = "swagger_eiffel_client"; @@ -49,9 +23,6 @@ public class EiffelClientCodegen extends DefaultCodegen implements CodegenConfig protected UUID uuid; protected UUID uuidTest; - private final Set parentModels = new HashSet<>(); - private final Multimap childrenByParent = ArrayListMultimap.create(); - @Override public CodegenType getTag() { return CodegenType.CLIENT; @@ -79,59 +50,6 @@ public class EiffelClientCodegen extends DefaultCodegen implements CodegenConfig apiTestTemplateFiles.put("test/api_test.mustache", ".e"); apiDocTemplateFiles.put("api_doc.mustache", ".md"); embeddedTemplateDir = templateDir = "Eiffel"; - - setReservedWordsLowerCase(Arrays.asList( - // language reserved words - "across", "agent", "alias", "all", "and", "as", "assign", "attribute", "check", "class", "convert", - "create", "Current", "debug", "deferred", "do", "else", "elseif", "end", "ensure", "expanded", "export", - "external", "False", "feature", "from", "frozen", "if", "implies", "inherit", "inspect", "invariant", - "like", "local", "loop", "not", "note", "obsolete", "old", "once", "only", "or", "Precursor", - "redefine", "rename", "require", "rescue", "Result", "retry", "select", "separate", "then", "True", - "TUPLE", "undefine", "until", "variant", "Void", "when", "xor")); - - defaultIncludes = new HashSet(Arrays.asList("map", "array")); - - languageSpecificPrimitives = new HashSet( - Arrays.asList("BOOLEAN", "INTEGER_8", "INTEGER_16", "INTEGER_32", "INTEGER_64", "NATURAL_8", - "NATURAL_16", "NATURAL_32", "NATURAL_64", "REAL_32", "REAL_64")); - - instantiationTypes.clear(); - - typeMapping.clear(); - typeMapping.put("integer", "INTEGER_32"); - typeMapping.put("long", "INTEGER_64"); - typeMapping.put("number", "REAL_32"); - typeMapping.put("float", "REAL_32"); - typeMapping.put("double", "REAL_64"); - typeMapping.put("boolean", "BOOLEAN"); - typeMapping.put("string", "STRING_32"); - typeMapping.put("UUID", "UUID"); // - typeMapping.put("date", "DATE"); - typeMapping.put("DateTime", "DATE_TIME"); - typeMapping.put("date-time", "DATE_TIME"); - typeMapping.put("password", "STRING"); - typeMapping.put("File", "FILE"); - typeMapping.put("file", "FILE"); - typeMapping.put("binary", "STRING_32"); - typeMapping.put("ByteArray", "ARRAY [NATURAL_8]"); - typeMapping.put("object", "ANY"); - typeMapping.put("map", "STRING_TABLE"); - typeMapping.put("array", "LIST"); - typeMapping.put("list", "LIST"); - - //instantiationTypes.put("array", "ARRAY"); - //instantiationTypes.put("list", "ARRAYED_LIST"); - //instantiationTypes.put("map", "STRING_TABLE"); - - - cliOptions.clear(); - cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Eiffel Cluster name (convention: lowercase).") - .defaultValue("swagger")); - cliOptions - .add(new CliOption(CodegenConstants.PACKAGE_VERSION, "Eiffel package version.").defaultValue("1.0.0")); - cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, - "hides the timestamp when files were generated").defaultValue(Boolean.TRUE.toString())); - } @Override @@ -173,6 +91,7 @@ public class EiffelClientCodegen extends DefaultCodegen implements CodegenConfig final String authFolder = ("src/framework/auth"); final String serializerFolder = ("src/framework/serialization"); supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml")); supportingFiles.add(new SupportingFile("ecf.mustache", "", "api_client.ecf")); supportingFiles.add(new SupportingFile("test/ecf_test.mustache", "test", "api_test.ecf")); supportingFiles.add(new SupportingFile("test/application.mustache", "test", "application.e")); @@ -205,24 +124,6 @@ public class EiffelClientCodegen extends DefaultCodegen implements CodegenConfig } - @Override - public String escapeReservedWord(String name) { - // Can't start with an underscore, as our fields need to start with an - // UppercaseLetter so that Go treats them as public/visible. - - // Options? - // - MyName - // - AName - // - TheName - // - XName - // - X_Name - // ... or maybe a suffix? - // - Name_ ... think this will work. - if (this.reservedWordsMappings().containsKey(name)) { - return this.reservedWordsMappings().get(name); - } - return camelize(name) + '_'; - } @Override public String apiFileFolder() { @@ -237,141 +138,6 @@ public class EiffelClientCodegen extends DefaultCodegen implements CodegenConfig return outputFolder + File.separator + "test" + File.separator + "apis"; } - @Override - public String toVarName(String name) { - // replace - with _ e.g. created-at => created_at - name = sanitizeName(name.replaceAll("-", "_")); - - // if it's all uppper case, do nothing - if (name.matches("^[A-Z_]*$")) { - return name; - } - - // pet_id - // petId => pet_id - name = unCamelize(name); - - // for reserved word or word starting with number, append _ - if (isReservedWord(name)) { - name = escapeReservedWord(name); - } - - // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { - name = escapeReservedWord(name); - } - - return name; - } - - @Override - public String toParamName(String name) { - // params should be lowercase. E.g. "person: PERSON" - return toVarName(name).toLowerCase(); - } - - @Override - public String toModelName(String name) { - // phone_number => PHONE_NUMBER - return toModelFilename(name).toUpperCase(); - } - - @Override - public String toModelFilename(String name) { - if (!StringUtils.isEmpty(modelNamePrefix)) { - name = modelNamePrefix + "_" + name; - } - - if (!StringUtils.isEmpty(modelNameSuffix)) { - name = name + "_" + modelNameSuffix; - } - - name = sanitizeName(name); - - // model name cannot use reserved keyword, e.g. return - if (isReservedWord(name)) { - LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + ("model_" + name)); - name = "model_" + name; // e.g. return => ModelReturn (after - // camelize) - } - - // model name starts with number - if (name.matches("^\\d.*")) { - LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " - + ("model_" + name)); - name = "model_" + name; // e.g. 200Response => Model200Response - // (after camelize) - } - - return underscore(name); - } - - @Override - public String toApiFilename(String name) { - // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); // FIXME: a parameter should not be - // assigned. Also declare the - // methods parameters as 'final'. - - // e.g. PetApi.go => pet_api.go - return underscore(name) + "_api"; - } - - @Override - public String toApiTestFilename(String name) { - return toApiName(name).toLowerCase() + "_test"; - } - - @Override - public String toApiName(String name) { - if (name.length() == 0) { - return "DEFAULT_API"; - } - return name.toUpperCase() + "_API"; - } - - /** - * Overrides postProcessParameter to add a vendor extension - * "x-exportParamName". This is useful when paramName starts with a - * lowercase letter, but we need that param to be exportable (starts with an - * Uppercase letter). - * - * @param parameter - * CodegenParameter object to be processed. - */ - @Override - public void postProcessParameter(CodegenParameter parameter) { - - // Give the base class a chance to process - super.postProcessParameter(parameter); - - char firstChar = parameter.paramName.charAt(0); - - if (Character.isUpperCase(firstChar)) { - // First char is already uppercase, just use paramName. - parameter.vendorExtensions.put("x-exportParamName", parameter.paramName); - - } - - // It's a lowercase first char, let's convert it to uppercase - StringBuilder sb = new StringBuilder(parameter.paramName); - sb.setCharAt(0, Character.toUpperCase(firstChar)); - parameter.vendorExtensions.put("x-exportParamName", sb.toString()); - } - - @Override - public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { - if (!isNullOrEmpty(model.parent)) { - parentModels.add(model.parent); - if (!childrenByParent.containsEntry(model.parent, model)) { - childrenByParent.put(model.parent, model); - } - } - if (!isNullOrEmpty(model.parentSchema)) { - model.parentSchema = model.parentSchema.toLowerCase(); - } - } - @Override public String apiDocFileFolder() { return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar); @@ -382,206 +148,6 @@ public class EiffelClientCodegen extends DefaultCodegen implements CodegenConfig return (outputFolder + "/" + modelDocPath).replace('/', File.separatorChar); } - @Override - public String toModelDocFilename(String name) { - return toModelName(name); - } - - @Override - public String toApiDocFilename(String name) { - return toApiName(name); - } - - @Override - public String getTypeDeclaration(Property p) { - if (p instanceof ArrayProperty) { - ArrayProperty ap = (ArrayProperty) p; - Property inner = ap.getItems(); - return "LIST [" + getTypeDeclaration(inner) + "]"; - } else if (p instanceof MapProperty) { - MapProperty mp = (MapProperty) p; - Property inner = mp.getAdditionalProperties(); - - return getSwaggerType(p) + "[" + getTypeDeclaration(inner) + "]"; - } - // return super.getTypeDeclaration(p); - - // Not using the supertype invocation, because we want to UpperCamelize - // the type. - String swaggerType = getSwaggerType(p); - if (typeMapping.containsKey(swaggerType)) { - return typeMapping.get(swaggerType); - } - - if (typeMapping.containsValue(swaggerType)) { - return swaggerType; - } - - if (languageSpecificPrimitives.contains(swaggerType)) { - return swaggerType; - } - - return toModelName(swaggerType); - } - - @Override - public String getSwaggerType(Property p) { - String swaggerType = super.getSwaggerType(p); - String type = null; - if (typeMapping.containsKey(swaggerType)) { - type = typeMapping.get(swaggerType); - if (languageSpecificPrimitives.contains(type)) - return (type); - } else - type = swaggerType; - return type; - } - - @Override - public String toOperationId(String operationId) { - String sanitizedOperationId = sanitizeName(operationId); - - // method name cannot use reserved keyword, e.g. return - if (isReservedWord(sanitizedOperationId)) { - LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " - + camelize("call_" + operationId)); - sanitizedOperationId = "call_" + sanitizedOperationId; - } - // method name from updateSomething to update_Something. - sanitizedOperationId = unCamelize(sanitizedOperationId); - - return toEiffelFeatureStyle(sanitizedOperationId); - } - - @Override - public Map postProcessOperations(Map objs) { - @SuppressWarnings("unchecked") - Map objectMap = (Map) objs.get("operations"); - @SuppressWarnings("unchecked") - List operations = (List) objectMap.get("operation"); - for (CodegenOperation operation : operations) { - // http method verb conversion (e.g. PUT => Put) - - operation.httpMethod = camelize(operation.httpMethod.toLowerCase()); - } - - // remove model imports to avoid error - List> imports = (List>) objs.get("imports"); - if (imports == null) - return objs; - - Iterator> iterator = imports.iterator(); - while (iterator.hasNext()) { - String _import = iterator.next().get("import"); - if (_import.startsWith(apiPackage())) - iterator.remove(); - } - // if the return type is not primitive, import encoding/json - for (CodegenOperation operation : operations) { - if (operation.returnBaseType != null && needToImport(operation.returnBaseType)) { - imports.add(createMapping("import", "encoding/json")); - break; // just need to import once - } - } - - // this will only import "fmt" if there are items in pathParams - for (CodegenOperation operation : operations) { - if (operation.pathParams != null && operation.pathParams.size() > 0) { - imports.add(createMapping("import", "fmt")); - break; // just need to import once - } - } - - // recursively add import for mapping one type to multiple imports - List> recursiveImports = (List>) objs.get("imports"); - if (recursiveImports == null) - return objs; - - ListIterator> listIterator = imports.listIterator(); - while (listIterator.hasNext()) { - String _import = listIterator.next().get("import"); - // if the import package happens to be found in the importMapping - // (key) - // add the corresponding import package to the list - if (importMapping.containsKey(_import)) { - listIterator.add(createMapping("import", importMapping.get(_import))); - } - } - - return objs; - } - - @Override - public Map postProcessModels(Map objs) { - // remove model imports to avoid error - List> imports = (List>) objs.get("imports"); - final String prefix = modelPackage(); - Iterator> iterator = imports.iterator(); - while (iterator.hasNext()) { - String _import = iterator.next().get("import"); - if (_import.startsWith(prefix)) - iterator.remove(); - } - - // recursively add import for mapping one type to multiple imports - List> recursiveImports = (List>) objs.get("imports"); - if (recursiveImports == null) - return objs; - - ListIterator> listIterator = imports.listIterator(); - while (listIterator.hasNext()) { - String _import = listIterator.next().get("import"); - // if the import package happens to be found in the importMapping - // (key) - // add the corresponding import package to the list - if (importMapping.containsKey(_import)) { - listIterator.add(createMapping("import", importMapping.get(_import))); - } - } - - return objs; - } - - @Override - public Map postProcessAllModels(final Map models) { - - final Map processed = super.postProcessAllModels(models); - postProcessParentModels(models); - return processed; - } - - private void postProcessParentModels(final Map models) { - for (final String parent : parentModels) { - final CodegenModel parentModel = ModelUtils.getModelByName(parent, models); - final Collection childrenModels = childrenByParent.get(parent); - for (final CodegenModel child : childrenModels) { - processParentPropertiesInChildModel(parentModel, child); - } - } - } - - /** - * Sets the child property's isInherited flag to true if it is an inherited - * property - */ - private void processParentPropertiesInChildModel(final CodegenModel parent, final CodegenModel child) { - final Map childPropertiesByName = new HashMap<>(child.vars.size()); - for (final CodegenProperty childProperty : child.vars) { - childPropertiesByName.put(childProperty.name, childProperty); - } - for (final CodegenProperty parentProperty : parent.vars) { - final CodegenProperty duplicatedByParent = childPropertiesByName.get(parentProperty.name); - if (duplicatedByParent != null) { - duplicatedByParent.isInherited = true; - } - } - } - - @Override - protected boolean needToImport(String type) { - return !defaultIncludes.contains(type) && !languageSpecificPrimitives.contains(type); - } - public void setPackageName(String packageName) { this.packageName = packageName; } @@ -590,34 +156,5 @@ public class EiffelClientCodegen extends DefaultCodegen implements CodegenConfig this.packageVersion = packageVersion; } - @Override - public String escapeQuotationMark(String input) { - // remove " to avoid code injection - return input.replace("\"", ""); - } - @Override - public String escapeUnsafeCharacters(String input) { - return input.replace("*/", "*_/").replace("/*", "/_*"); - } - - public Map createMapping(String key, String value) { - Map customImport = new HashMap(); - customImport.put(key, value); - - return customImport; - } - - public String unCamelize(String name) { - return name.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase(); - } - - public String toEiffelFeatureStyle(String operationId) { - if (operationId.startsWith("get_")) { - return operationId.substring(4, operationId.length()); - } else { - return operationId; - } - - } } diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/README.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/README.mustache index b0e787e8fd9..9143997024d 100644 --- a/modules/swagger-codegen/src/main/resources/Eiffel/README.mustache +++ b/modules/swagger-codegen/src/main/resources/Eiffel/README.mustache @@ -29,12 +29,12 @@ All URIs are relative to *{{basePath}}* Class | Method | HTTP request | Description ------------ | ------------- | ------------- | ------------- -{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}/{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} {{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} ## Documentation For Models -{{#models}}{{#model}} - [{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) +{{#models}}{{#model}} - [{{{classname}}}]({{modelDocPath}}/{{{classname}}}.md) {{/model}}{{/models}} ## Documentation For Authorization diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/api_client.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/api_client.mustache index 1e8845b6f51..330a0c61a2d 100644 --- a/modules/swagger-codegen/src/main/resources/Eiffel/api_client.mustache +++ b/modules/swagger-codegen/src/main/resources/Eiffel/api_client.mustache @@ -1,4 +1,4 @@ -{{>noteInfo}} +{{>noteinfo}} class API_CLIENT diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/framework/auth/authentication.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/framework/auth/authentication.mustache index 19c0b046779..8e274812fb5 100644 --- a/modules/swagger-codegen/src/main/resources/Eiffel/framework/auth/authentication.mustache +++ b/modules/swagger-codegen/src/main/resources/Eiffel/framework/auth/authentication.mustache @@ -1,4 +1,4 @@ -{{>noteInfo}} +{{>noteinfo}} deferred class AUTHENTICATION diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/travis.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/travis.mustache new file mode 100644 index 00000000000..6cb4545088a --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Eiffel/travis.mustache @@ -0,0 +1,20 @@ +language: eiffel +before_script: + - export current_dir=$(pwd) + - echo current_dir + - cd .. + - wget https://ftp.eiffel.com/pub/beta/nightly/Eiffel_17.11_gpl_100608-linux-x86-64.tar.bz2 + - tar -xvf Eiffel_17.11_gpl_100608-linux-x86-64.tar.bz2 + - export ISE_EIFFEL=$PWD/Eiffel_17.11 + - export ISE_PLATFORM=linux-x86-64 + - export PATH=$PATH:$ISE_EIFFEL/studio/spec/$ISE_PLATFORM/bin + - export PATH=$PATH:$ISE_EIFFEL/tools/spec/$ISE_PLATFORM/bin + - cd $current_dir + +# safelist +branches: + only: + - master + +script: compile_all -ecb -melt -list_failures -clean -options dotnet=false +