From 83fb107749b6dcde851baf91cd2bb8d6120796d8 Mon Sep 17 00:00:00 2001 From: Ben Wells Date: Tue, 19 Dec 2017 15:41:02 +0000 Subject: [PATCH] Create abstract go codegen base class (#7197) Remove tabs --- bin/windows/go-petstore.bat | 2 +- .../codegen/languages/AbstractGoCodegen.java | 453 +++++++++++++++++ .../codegen/languages/GoClientCodegen.java | 460 ++---------------- .../codegen/languages/GoServerCodegen.java | 457 +---------------- .../petstore/go-api-server/go/README.md | 2 +- 5 files changed, 501 insertions(+), 873 deletions(-) create mode 100644 modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractGoCodegen.java diff --git a/bin/windows/go-petstore.bat b/bin/windows/go-petstore.bat index 11b2ec22d92..168263a45a7 100755 --- a/bin/windows/go-petstore.bat +++ b/bin/windows/go-petstore.bat @@ -5,6 +5,6 @@ If Not Exist %executable% ( ) 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 go -o samples\client\petstore\go -DpackageName=petstore +set ags=generate -t modules\swagger-codegen\src\main\resources\go -i modules\swagger-codegen\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -l go -o samples\client\petstore\go\go-petstore -DpackageName=petstore java %JAVA_OPTS% -jar %executable% %ags% diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractGoCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractGoCodegen.java new file mode 100644 index 00000000000..b03a6a0b30a --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractGoCodegen.java @@ -0,0 +1,453 @@ +package io.swagger.codegen.languages; + +import io.swagger.codegen.*; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.Property; +import io.swagger.models.parameters.Parameter; +import io.swagger.util.Yaml; + +import java.util.*; + +import org.apache.commons.lang3.StringUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AbstractGoCodegen extends DefaultCodegen implements CodegenConfig { + + protected static Logger LOGGER = LoggerFactory.getLogger(AbstractGoCodegen.class); + + protected String packageName = "swagger"; + + public AbstractGoCodegen() { + super(); + + defaultIncludes = new HashSet( + Arrays.asList( + "map", + "array") + ); + + languageSpecificPrimitives = new HashSet( + Arrays.asList( + "string", + "bool", + "uint", + "uint32", + "uint64", + "int", + "int32", + "int64", + "float32", + "float64", + "complex64", + "complex128", + "rune", + "byte") + ); + + instantiationTypes.clear(); + /*instantiationTypes.put("array", "GoArray"); + instantiationTypes.put("map", "GoMap");*/ + + typeMapping.clear(); + typeMapping.put("integer", "int32"); + typeMapping.put("long", "int64"); + typeMapping.put("number", "float32"); + typeMapping.put("float", "float32"); + typeMapping.put("double", "float64"); + typeMapping.put("boolean", "bool"); + typeMapping.put("string", "string"); + typeMapping.put("UUID", "string"); + typeMapping.put("date", "string"); + typeMapping.put("DateTime", "time.Time"); + typeMapping.put("password", "string"); + typeMapping.put("File", "*os.File"); + typeMapping.put("file", "*os.File"); + // map binary to string as a workaround + // the correct solution is to use []byte + typeMapping.put("binary", "string"); + typeMapping.put("ByteArray", "string"); + typeMapping.put("object", "interface{}"); + typeMapping.put("UUID", "string"); + + importMapping = new HashMap(); + importMapping.put("time.Time", "time"); + importMapping.put("*os.File", "os"); + + cliOptions.clear(); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Go package name (convention: lowercase).") + .defaultValue("swagger")); + cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "hides the timestamp when files were generated") + .defaultValue(Boolean.TRUE.toString())); + + } + + /** + * Escapes a reserved word as defined in the `reservedWords` array. Handle escaping + * those terms here. This logic is only called if a variable matches the reserved words + * + * @return the escaped term + */ + @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 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; + + // camelize (lower first character) the variable name + // pet_id => PetId + name = camelize(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 (name.matches("^\\d.*")) + name = "Var" + name; + + return name; + } + + @Override + public String toParamName(String name) { + // params should be lowerCamelCase. E.g. "person Person", instead of + // "Person Person". + // + // REVISIT: Actually, for idiomatic go, the param name should + // really should just be a letter, e.g. "p Person"), but we'll get + // around to that some other time... Maybe. + return camelize(toVarName(name), true); + } + + @Override + public String toModelName(String name) { + // camelize the model name + // phone_number => PhoneNumber + return camelize(toModelFilename(name)); + } + + @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"; + } + + /** + * 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 String getTypeDeclaration(Property p) { + if(p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + return "[]" + getTypeDeclaration(inner); + } + else if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + + return getSwaggerType(p) + "[string]" + getTypeDeclaration(inner); + } + //return super.getTypeDeclaration(p); + + // 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; + } + + return camelize(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 their is a return type, import encoding/json + for (CodegenOperation operation : operations) { + if(operation.returnBaseType != null ) { + 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 postProcessModelsEnum(objs); + } + + @Override + protected boolean needToImport(String type) { + return !defaultIncludes.contains(type) + && !languageSpecificPrimitives.contains(type); + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + @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 toEnumValue(String value, String datatype) { + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + return value; + } else { + return escapeText(value); + } + } + + @Override + public String toEnumDefaultValue(String value, String datatype) { + return datatype + "_" + value; + } + + @Override + public String toEnumVarName(String name, String datatype) { + if (name.length() == 0) { + return "EMPTY"; + } + + // number + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + String varName = name; + varName = varName.replaceAll("-", "MINUS_"); + varName = varName.replaceAll("\\+", "PLUS_"); + varName = varName.replaceAll("\\.", "_DOT_"); + return varName; + } + + // for symbol, e.g. $, # + if (getSymbolName(name) != null) { + return getSymbolName(name).toUpperCase(); + } + + // string + String enumName = sanitizeName(underscore(name).toUpperCase()); + enumName = enumName.replaceFirst("^_", ""); + enumName = enumName.replaceFirst("_$", ""); + + if (isReservedWord(enumName) || enumName.matches("\\d.*")) { // reserved word or starts with number + return escapeReservedWord(enumName); + } else { + return enumName; + } + } + + @Override + public String toEnumName(CodegenProperty property) { + String enumName = underscore(toModelName(property.name)).toUpperCase(); + + // remove [] for array or map of enum + enumName = enumName.replace("[]", ""); + + if (enumName.matches("\\d.*")) { // starts with number + return "_" + enumName; + } else { + return enumName; + } + } + +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoClientCodegen.java index dad601cc051..c974e6928c2 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoClientCodegen.java @@ -14,28 +14,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class GoClientCodegen extends DefaultCodegen implements CodegenConfig { - static Logger LOGGER = LoggerFactory.getLogger(GoClientCodegen.class); +public class GoClientCodegen extends AbstractGoCodegen { - protected String packageName = "swagger"; protected String packageVersion = "1.0.0"; protected String apiDocPath = "docs/"; protected String modelDocPath = "docs/"; - public CodegenType getTag() { - return CodegenType.CLIENT; - } - - public String getName() { - return "go"; - } - - public String getHelp() { - return "Generates a Go client library (beta)."; - } - public GoClientCodegen() { super(); + outputFolder = "generated-code/go"; modelTemplateFiles.put("model.mustache", ".go"); apiTemplateFiles.put("api.mustache", ".go"); @@ -60,65 +47,8 @@ public GoClientCodegen() { // Added "error" as it's used so frequently that it may as well be a keyword ); - defaultIncludes = new HashSet( - Arrays.asList( - "map", - "array") - ); - - languageSpecificPrimitives = new HashSet( - Arrays.asList( - "string", - "bool", - "uint", - "uint32", - "uint64", - "int", - "int32", - "int64", - "float32", - "float64", - "complex64", - "complex128", - "rune", - "byte") - ); - - instantiationTypes.clear(); - /*instantiationTypes.put("array", "GoArray"); - instantiationTypes.put("map", "GoMap");*/ - - typeMapping.clear(); - typeMapping.put("integer", "int32"); - typeMapping.put("long", "int64"); - typeMapping.put("number", "float32"); - typeMapping.put("float", "float32"); - typeMapping.put("double", "float64"); - typeMapping.put("boolean", "bool"); - typeMapping.put("string", "string"); - typeMapping.put("UUID", "string"); - typeMapping.put("date", "string"); - typeMapping.put("DateTime", "time.Time"); - typeMapping.put("password", "string"); - typeMapping.put("File", "*os.File"); - typeMapping.put("file", "*os.File"); - // map binary to string as a workaround - // the correct solution is to use []byte - typeMapping.put("binary", "string"); - typeMapping.put("ByteArray", "string"); - typeMapping.put("object", "interface{}"); - - importMapping = new HashMap(); - importMapping.put("time.Time", "time"); - importMapping.put("*os.File", "os"); - - cliOptions.clear(); - cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Go package name (convention: lowercase).") - .defaultValue("swagger")); cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, "Go 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())); } @@ -166,138 +96,51 @@ public void processOpts() { supportingFiles.add(new SupportingFile(".travis.yml", "", ".travis.yml")); } + /** + * Configures the type of generator. + * + * @return the CodegenType for this generator + * @see io.swagger.codegen.CodegenType + */ @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() { - return outputFolder + File.separator; - } - - public String modelFileFolder() { - return outputFolder + File.separator; - } - - @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; - - // camelize (lower first character) the variable name - // pet_id => PetId - name = camelize(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 (name.matches("^\\d.*")) - name = "Var" + name; - - return name; - } - - @Override - public String toParamName(String name) { - // params should be lowerCamelCase. E.g. "person Person", instead of - // "Person Person". - // - // REVISIT: Actually, for idiomatic go, the param name should - // really should just be a letter, e.g. "p Person"), but we'll get - // around to that some other time... Maybe. - return camelize(toVarName(name), true); - } - - @Override - public String toModelName(String name) { - // camelize the model name - // phone_number => PhoneNumber - return camelize(toModelFilename(name)); + public CodegenType getTag() { + return CodegenType.CLIENT; } + /** + * Configures a friendly name for the generator. This will be used by the generator + * to select the library with the -l flag. + * + * @return the friendly name for the generator + */ @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); + public String getName() { + return "go"; } + /** + * Returns human-friendly help for the generator. Provide the consumer with help + * tips, parameters here + * + * @return A string value for the help message + */ @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"; + public String getHelp() { + return "Generates a Go client library (beta)."; } /** - * 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. + * Location to write api files. You can use the apiPackage() as defined when the class is + * instantiated */ @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); - - } + public String apiFileFolder() { + return outputFolder + File.separator; + } - // 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 String modelFileFolder() { + return outputFolder + File.separator; } @Override @@ -320,243 +163,8 @@ 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 "[]" + getTypeDeclaration(inner); - } - else if (p instanceof MapProperty) { - MapProperty mp = (MapProperty) p; - Property inner = mp.getAdditionalProperties(); - - return getSwaggerType(p) + "[string]" + getTypeDeclaration(inner); - } - //return super.getTypeDeclaration(p); - - // 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; - } - - return camelize(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 their is a return type, import encoding/json - for (CodegenOperation operation : operations) { - if(operation.returnBaseType != null ) { - 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 postProcessModelsEnum(objs); - } - - @Override - protected boolean needToImport(String type) { - return !defaultIncludes.contains(type) - && !languageSpecificPrimitives.contains(type); - } - - public void setPackageName(String packageName) { - this.packageName = packageName; - } - public void setPackageVersion(String packageVersion) { 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; - } - - - @Override - public String toEnumValue(String value, String datatype) { - if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { - return value; - } else { - return escapeText(value); - } - } - - @Override - public String toEnumDefaultValue(String value, String datatype) { - return datatype + "_" + value; - } - - @Override - public String toEnumVarName(String name, String datatype) { - if (name.length() == 0) { - return "EMPTY"; - } - - // number - if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { - String varName = name; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); - return varName; - } - - // for symbol, e.g. $, # - if (getSymbolName(name) != null) { - return getSymbolName(name).toUpperCase(); - } - - // string - String enumName = sanitizeName(underscore(name).toUpperCase()); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); - - if (isReservedWord(enumName) || enumName.matches("\\d.*")) { // reserved word or starts with number - return escapeReservedWord(enumName); - } else { - return enumName; - } - } - - @Override - public String toEnumName(CodegenProperty property) { - String enumName = underscore(toModelName(property.name)).toUpperCase(); - - // remove [] for array or map of enum - enumName = enumName.replace("[]", ""); - - if (enumName.matches("\\d.*")) { // starts with number - return "_" + enumName; - } else { - return enumName; - } - } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoServerCodegen.java index 4829cd11b8d..b8cbd510b75 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoServerCodegen.java @@ -1,36 +1,29 @@ package io.swagger.codegen.languages; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; import io.swagger.codegen.*; -import io.swagger.models.*; -import io.swagger.util.Yaml; import io.swagger.models.properties.ArrayProperty; import io.swagger.models.properties.MapProperty; import io.swagger.models.properties.Property; import io.swagger.models.parameters.Parameter; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.swagger.models.*; +import io.swagger.util.Yaml; import java.io.File; -import java.io.IOException; -import java.math.BigDecimal; import java.util.*; -import java.util.Map.Entry; + import org.apache.commons.lang3.StringUtils; -public class GoServerCodegen extends DefaultCodegen implements CodegenConfig { +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; - private static final Logger LOGGER = LoggerFactory.getLogger(GoServerCodegen.class); +public class GoServerCodegen extends AbstractGoCodegen { - protected String packageName = "swagger"; protected String apiVersion = "1.0.0"; protected int serverPort = 8080; protected String projectName = "swagger-server"; @@ -84,67 +77,6 @@ public GoServerCodegen() { "continue", "for", "import", "return", "var", "error", "nil") // Added "error" as it's used so frequently that it may as well be a keyword ); - - defaultIncludes = new HashSet( - Arrays.asList( - "map", - "array") - ); - - languageSpecificPrimitives = new HashSet( - Arrays.asList( - "string", - "bool", - "uint", - "uint32", - "uint64", - "int", - "int32", - "int64", - "float32", - "float64", - "complex64", - "complex128", - "rune", - "byte") - ); - - instantiationTypes.clear(); - /*instantiationTypes.put("array", "GoArray"); - instantiationTypes.put("map", "GoMap");*/ - - typeMapping.clear(); - typeMapping.put("integer", "int32"); - typeMapping.put("long", "int64"); - typeMapping.put("number", "float32"); - typeMapping.put("float", "float32"); - typeMapping.put("double", "float64"); - typeMapping.put("boolean", "bool"); - typeMapping.put("string", "string"); - typeMapping.put("UUID", "string"); - typeMapping.put("date", "string"); - typeMapping.put("DateTime", "time.Time"); - typeMapping.put("password", "string"); - typeMapping.put("File", "*os.File"); - typeMapping.put("file", "*os.File"); - // map binary to string as a workaround - // the correct solution is to use []byte - typeMapping.put("binary", "string"); - typeMapping.put("ByteArray", "string"); - typeMapping.put("object", "interface{}"); - typeMapping.put("UUID", "string"); - - importMapping = new HashMap(); - importMapping.put("time.Time", "time"); - importMapping.put("*os.File", "os"); - importMapping.put("os", "io/ioutil"); - - cliOptions.clear(); - cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Go package name (convention: lowercase).") - .defaultValue("swagger")); - cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "hides the timestamp when files were generated") - .defaultValue(Boolean.TRUE.toString())); - } @Override @@ -221,32 +153,6 @@ public String getHelp() { "it will also generate service classes--which you can disable with the `-Dnoservice` environment variable."; } - /** - * Escapes a reserved word as defined in the `reservedWords` array. Handle escaping - * those terms here. This logic is only called if a variable matches the reserved words - * - * @return the escaped term - */ - @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) + '_'; - } - /** * Location to write api files. You can use the apiPackage() as defined when the class is * instantiated @@ -261,228 +167,6 @@ public String modelFileFolder() { return outputFolder + File.separator + apiPackage().replace('.', File.separatorChar); } - @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; - - // camelize (lower first character) the variable name - // pet_id => PetId - name = camelize(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 (name.matches("^\\d.*")) - name = "Var" + name; - - return name; - } - - @Override - public String toParamName(String name) { - // params should be lowerCamelCase. E.g. "person Person", instead of - // "Person Person". - // - // REVISIT: Actually, for idiomatic go, the param name should - // really should just be a letter, e.g. "p Person"), but we'll get - // around to that some other time... Maybe. - return camelize(toVarName(name), true); - } - - @Override - public String toModelName(String name) { - // camelize the model name - // phone_number => PhoneNumber - return camelize(toModelFilename(name)); - } - - @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"; - } - - /** - * 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 String getTypeDeclaration(Property p) { - if(p instanceof ArrayProperty) { - ArrayProperty ap = (ArrayProperty) p; - Property inner = ap.getItems(); - return "[]" + getTypeDeclaration(inner); - } - else if (p instanceof MapProperty) { - MapProperty mp = (MapProperty) p; - Property inner = mp.getAdditionalProperties(); - - return getSwaggerType(p) + "[string]" + getTypeDeclaration(inner); - } - //return super.getTypeDeclaration(p); - - // 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; - } - - return camelize(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 their is a return type, import encoding/json - for (CodegenOperation operation : operations) { - if(operation.returnBaseType != null ) { - 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 postProcessSupportingFileData(Map objs) { Swagger swagger = (Swagger)objs.get("swagger"); @@ -496,121 +180,4 @@ public Map postProcessSupportingFileData(Map obj return super.postProcessSupportingFileData(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 postProcessModelsEnum(objs); - } - - @Override - protected boolean needToImport(String type) { - return !defaultIncludes.contains(type) - && !languageSpecificPrimitives.contains(type); - } - - public void setPackageName(String packageName) { - this.packageName = packageName; - } - @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 toEnumValue(String value, String datatype) { - if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { - return value; - } else { - return escapeText(value); - } - } - - @Override - public String toEnumDefaultValue(String value, String datatype) { - return datatype + "_" + value; - } - - @Override - public String toEnumVarName(String name, String datatype) { - if (name.length() == 0) { - return "EMPTY"; - } - - // number - if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { - String varName = name; - varName = varName.replaceAll("-", "MINUS_"); - varName = varName.replaceAll("\\+", "PLUS_"); - varName = varName.replaceAll("\\.", "_DOT_"); - return varName; - } - - // for symbol, e.g. $, # - if (getSymbolName(name) != null) { - return getSymbolName(name).toUpperCase(); - } - - // string - String enumName = sanitizeName(underscore(name).toUpperCase()); - enumName = enumName.replaceFirst("^_", ""); - enumName = enumName.replaceFirst("_$", ""); - - if (isReservedWord(enumName) || enumName.matches("\\d.*")) { // reserved word or starts with number - return escapeReservedWord(enumName); - } else { - return enumName; - } - } - - @Override - public String toEnumName(CodegenProperty property) { - String enumName = underscore(toModelName(property.name)).toUpperCase(); - - // remove [] for array or map of enum - enumName = enumName.replace("[]", ""); - - if (enumName.matches("\\d.*")) { // starts with number - return "_" + enumName; - } else { - return enumName; - } - } - } diff --git a/samples/server/petstore/go-api-server/go/README.md b/samples/server/petstore/go-api-server/go/README.md index 685bcc7c19e..307542639a5 100644 --- a/samples/server/petstore/go-api-server/go/README.md +++ b/samples/server/petstore/go-api-server/go/README.md @@ -13,7 +13,7 @@ To see how to make this your own, look here: [README](https://github.com/swagger-api/swagger-codegen/blob/master/README.md) - API version: 1.0.0 -- Build date: 2017-12-17T09:38:04.917Z +- Build date: 2017-12-18T09:35:47.160Z ### Running the server