Output models for go-server #3851 (#7188)

* Output swagger models.

Fix route verbs and formatting

* Remove backup files

* Make postProcessOperations consistent with go client
This commit is contained in:
Ben Wells
2017-12-17 13:55:20 +00:00
committed by William Cheng
parent 8f2ed1c503
commit d25341d58d
23 changed files with 1380 additions and 150 deletions

View File

@@ -30,6 +30,7 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(GoServerCodegen.class);
protected String packageName = "swagger";
protected String apiVersion = "1.0.0";
protected int serverPort = 8080;
protected String projectName = "swagger-server";
@@ -47,7 +48,9 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
* for multiple files for model, just put another entry in the `modelTemplateFiles` with
* a different extension
*/
modelTemplateFiles.clear();
modelTemplateFiles.put(
"model.mustache",
".go");
/*
* Api classes. You can write classes for each Api file with the apiTemplateFiles map.
@@ -55,7 +58,7 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
* class
*/
apiTemplateFiles.put(
"controller.mustache", // the template to use
"controller-api.mustache", // the template to use
".go"); // the extension for each file to write
/*
@@ -78,8 +81,7 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
"case", "defer", "go", "map", "struct",
"chan", "else", "goto", "package", "switch",
"const", "fallthrough", "if", "range", "type",
"continue", "for", "import", "return", "var", "error", "ApiResponse",
"nil")
"continue", "for", "import", "return", "var", "error", "nil")
// Added "error" as it's used so frequently that it may as well be a keyword
);
@@ -143,6 +145,19 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "hides the timestamp when files were generated")
.defaultValue(Boolean.TRUE.toString()));
}
@Override
public void processOpts() {
super.processOpts();
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) {
setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME));
}
else {
setPackageName("swagger");
}
/*
* Additional Properties. These values can be passed to the templates and
* are available in models, apis, and supporting files
@@ -150,19 +165,20 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
additionalProperties.put("apiVersion", apiVersion);
additionalProperties.put("serverPort", serverPort);
additionalProperties.put("apiPath", apiPath);
additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName);
modelPackage = packageName;
apiPackage = packageName;
/*
* Supporting Files. You can write single files for the generator with the
* entire object tree available. If the input file has a suffix of `.mustache
* it will be processed by the template engine. Otherwise, it will be copied
*/
supportingFiles.add(new SupportingFile("swagger.mustache",
"api",
"swagger.yaml")
);
supportingFiles.add(new SupportingFile("swagger.mustache", "api", "swagger.yaml"));
supportingFiles.add(new SupportingFile("main.mustache", "", "main.go"));
supportingFiles.add(new SupportingFile("routers.mustache", apiPath, "routers.go"));
supportingFiles.add(new SupportingFile("logger.mustache", apiPath, "logger.go"));
supportingFiles.add(new SupportingFile("app.mustache", apiPath, "app.yaml"));
writeOptional(outputFolder, new SupportingFile("README.mustache", apiPath, "README.md"));
}
@@ -205,14 +221,6 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
"it will also generate service classes--which you can disable with the `-Dnoservice` environment variable.";
}
@Override
public String toApiName(String name) {
if (name.length() == 0) {
return "DefaultController";
}
return initialCaps(name);
}
/**
* 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
@@ -220,11 +228,23 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
* @return the escaped term
*/
@Override
public String escapeReservedWord(String name) {
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 "_" + name; // add an underscore to the name
return camelize(name) + '_';
}
/**
@@ -236,6 +256,46 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
return outputFolder + File.separator + apiPackage().replace('.', File.separatorChar);
}
@Override
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
@@ -243,36 +303,67 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
return camelize(toModelFilename(name));
}
@Override
public String toOperationId(String operationId) {
// method name cannot use reserved keyword, e.g. return
if (isReservedWord(operationId)) {
LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + camelize(sanitizeName("call_" + operationId)));
operationId = "call_" + operationId;
}
return camelize(operationId);
}
@Override
public String toModelFilename(String name) {
if (!StringUtils.isEmpty(modelNamePrefix)) {
name = modelNamePrefix + name;
name = modelNamePrefix + "_" + name;
}
if (!StringUtils.isEmpty(modelNameSuffix)) {
name = name + 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 " + camelize("model_" + 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)
}
return camelize(name);
// 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
@@ -323,14 +414,127 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
}
@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'.
public String toOperationId(String operationId) {
String sanitizedOperationId = sanitizeName(operationId);
// e.g. PetApi.go => pet_api.go
return underscore(name);
// 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<String, Object> postProcessOperations(Map<String, Object> objs) {
@SuppressWarnings("unchecked")
Map<String, Object> objectMap = (Map<String, Object>) objs.get("operations");
@SuppressWarnings("unchecked")
List<CodegenOperation> operations = (List<CodegenOperation>) objectMap.get("operation");
for (CodegenOperation operation : operations) {
// http method verb conversion (e.g. PUT => Put)
operation.httpMethod = camelize(operation.httpMethod.toLowerCase());
}
// remove model imports to avoid error
List<Map<String, String>> imports = (List<Map<String, String>>) objs.get("imports");
if (imports == null)
return objs;
Iterator<Map<String, String>> 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<Map<String, String>> recursiveImports = (List<Map<String, String>>) objs.get("imports");
if (recursiveImports == null)
return objs;
ListIterator<Map<String, String>> 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<String, Object> postProcessSupportingFileData(Map<String, Object> objs) {
Swagger swagger = (Swagger)objs.get("swagger");
if(swagger != null) {
try {
objs.put("swagger-yaml", Yaml.mapper().writeValueAsString(swagger));
} catch (JsonProcessingException e) {
LOGGER.error(e.getMessage(), e);
}
}
return super.postProcessSupportingFileData(objs);
}
@Override
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
// remove model imports to avoid error
List<Map<String, String>> imports = (List<Map<String, String>>) objs.get("imports");
final String prefix = modelPackage();
Iterator<Map<String, String>> 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<Map<String, String>> recursiveImports = (List<Map<String, String>>) objs.get("imports");
if (recursiveImports == null)
return objs;
ListIterator<Map<String, String>> 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
@@ -342,12 +546,19 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
return input.replace("*/", "*_/").replace("/*", "/_*");
}
public Map<String, String> createMapping(String key, String value){
Map<String, String> customImport = new HashMap<String, String>();
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) + "\'";
return escapeText(value);
}
}
@@ -402,9 +613,4 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig {
}
}
@Override
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
// process enum in models
return postProcessModelsEnum(objs);
}
}

View File

@@ -1 +0,0 @@
application: {{packageName}}

View File

@@ -1,12 +1,10 @@
{{>partial_header}}
package {{packageName}}
{{#operations}}
import (
"net/http"
)
type {{classname}} struct {
}{{#operation}}
){{#operation}}
func {{nickname}}(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")

View File

@@ -1,3 +1,4 @@
{{>partial_header}}
package {{packageName}}
import (

View File

@@ -1,3 +1,4 @@
{{>partial_header}}
package main
import (

View File

@@ -0,0 +1,24 @@
{{>partial_header}}
package {{packageName}}
{{#models}}{{#imports}}
import ({{/imports}}{{#imports}}
"{{import}}"{{/imports}}{{#imports}}
)
{{/imports}}{{#model}}{{#isEnum}}{{#description}}// {{{classname}}} : {{{description}}}{{/description}}
type {{{name}}} {{^format}}{{dataType}}{{/format}}{{#format}}{{{format}}}{{/format}}
// List of {{{name}}}
const (
{{#allowableValues}}
{{#enumVars}}
{{name}} {{{classname}}} = "{{{value}}}"
{{/enumVars}}
{{/allowableValues}}
){{/isEnum}}{{^isEnum}}{{#description}}
// {{{description}}}{{/description}}
type {{classname}} struct {
{{#vars}}{{#description}}
// {{{description}}}{{/description}}
{{name}} {{^isEnum}}{{^isPrimitiveType}}{{^isContainer}}{{^isDateTime}}*{{/isDateTime}}{{/isContainer}}{{/isPrimitiveType}}{{/isEnum}}{{{datatype}}} `json:"{{baseName}}{{^required}},omitempty{{/required}}"`
{{/vars}}
}{{/isEnum}}{{/model}}{{/models}}

View File

@@ -0,0 +1,17 @@
/*
{{#appName}}
* {{{appName}}}
*
{{/appName}}
{{#appDescription}}
* {{{appDescription}}}
*
{{/appDescription}}
{{#version}}
* API version: {{{version}}}
{{/version}}
{{#infoEmail}}
* Contact: {{{infoEmail}}}
{{/infoEmail}}
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/

View File

@@ -1,8 +1,10 @@
{{>partial_header}}
package {{packageName}}
import (
"fmt"
"net/http"
"strings"
"github.com/gorilla/mux"
)
@@ -47,7 +49,7 @@ var routes = Routes{
Route{
"{{operationId}}",
"{{httpMethod}}",
strings.ToUpper("{{httpMethod}}"),
"{{{basePathWithoutHost}}}{{{path}}}",
{{operationId}},
},{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}