|
|
|
|
@@ -0,0 +1,274 @@
|
|
|
|
|
package io.swagger.codegen.languages;
|
|
|
|
|
|
|
|
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
|
|
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.Operation;
|
|
|
|
|
import io.swagger.models.Path;
|
|
|
|
|
import io.swagger.models.Swagger;
|
|
|
|
|
import io.swagger.util.Yaml;
|
|
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
|
|
public class FlaskConnexionCodegen extends DefaultCodegen implements CodegenConfig {
|
|
|
|
|
protected String apiVersion = "1.0.0";
|
|
|
|
|
protected int serverPort = 8080;
|
|
|
|
|
protected String projectName = "swagger-server";
|
|
|
|
|
|
|
|
|
|
public FlaskConnexionCodegen() {
|
|
|
|
|
super();
|
|
|
|
|
|
|
|
|
|
languageSpecificPrimitives.clear();
|
|
|
|
|
languageSpecificPrimitives.add("int");
|
|
|
|
|
languageSpecificPrimitives.add("float");
|
|
|
|
|
languageSpecificPrimitives.add("list");
|
|
|
|
|
languageSpecificPrimitives.add("bool");
|
|
|
|
|
languageSpecificPrimitives.add("str");
|
|
|
|
|
languageSpecificPrimitives.add("datetime");
|
|
|
|
|
languageSpecificPrimitives.add("date");
|
|
|
|
|
|
|
|
|
|
typeMapping.clear();
|
|
|
|
|
typeMapping.put("integer", "int");
|
|
|
|
|
typeMapping.put("float", "float");
|
|
|
|
|
typeMapping.put("number", "float");
|
|
|
|
|
typeMapping.put("long", "int");
|
|
|
|
|
typeMapping.put("double", "float");
|
|
|
|
|
typeMapping.put("array", "list");
|
|
|
|
|
typeMapping.put("map", "dict");
|
|
|
|
|
typeMapping.put("boolean", "bool");
|
|
|
|
|
typeMapping.put("string", "str");
|
|
|
|
|
typeMapping.put("date", "date");
|
|
|
|
|
typeMapping.put("DateTime", "datetime");
|
|
|
|
|
typeMapping.put("object", "object");
|
|
|
|
|
typeMapping.put("file", "file");
|
|
|
|
|
|
|
|
|
|
// set the output folder here
|
|
|
|
|
outputFolder = "generated-code/connexion";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Models. You can write model files using the modelTemplateFiles map.
|
|
|
|
|
* if you want to create one template for file, you can do so here.
|
|
|
|
|
* for multiple files for model, just put another entry in the `modelTemplateFiles` with
|
|
|
|
|
* a different extension
|
|
|
|
|
*/
|
|
|
|
|
modelTemplateFiles.clear();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Api classes. You can write classes for each Api file with the apiTemplateFiles map.
|
|
|
|
|
* as with models, add multiple entries with different extensions for multiple files per
|
|
|
|
|
* class
|
|
|
|
|
*/
|
|
|
|
|
apiTemplateFiles.put(
|
|
|
|
|
"controller.mustache", // the template to use
|
|
|
|
|
".py"); // the extension for each file to write
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Template Location. This is the location which templates will be read from. The generator
|
|
|
|
|
* will use the resource stream to attempt to read the templates.
|
|
|
|
|
*/
|
|
|
|
|
embeddedTemplateDir = templateDir = "flaskConnexion";
|
|
|
|
|
|
|
|
|
|
// from https://docs.python.org/release/2.5.4/ref/keywords.html
|
|
|
|
|
reservedWords = new HashSet<String>(
|
|
|
|
|
Arrays.asList(
|
|
|
|
|
"and", "del", "from", "not", "while", "as", "elif", "global", "or", "with",
|
|
|
|
|
"assert", "else", "if", "pass", "yield", "break", "except", "import",
|
|
|
|
|
"print", "class", "exec", "in", "raise", "continue", "finally", "is",
|
|
|
|
|
"return", "def", "for", "lambda", "try"));
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Additional Properties. These values can be passed to the templates and
|
|
|
|
|
* are available in models, apis, and supporting files
|
|
|
|
|
*/
|
|
|
|
|
additionalProperties.put("apiVersion", apiVersion);
|
|
|
|
|
additionalProperties.put("serverPort", serverPort);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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",
|
|
|
|
|
"swagger",
|
|
|
|
|
"swagger.yaml")
|
|
|
|
|
);
|
|
|
|
|
supportingFiles.add(new SupportingFile("app.mustache",
|
|
|
|
|
"",
|
|
|
|
|
"app.py")
|
|
|
|
|
);
|
|
|
|
|
supportingFiles.add(new SupportingFile("README.mustache",
|
|
|
|
|
"",
|
|
|
|
|
"README.md")
|
|
|
|
|
);
|
|
|
|
|
supportingFiles.add(new SupportingFile("controller.mustache",
|
|
|
|
|
"controllers",
|
|
|
|
|
"default_controller.py")
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String apiPackage() {
|
|
|
|
|
return "controllers";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configures the type of generator.
|
|
|
|
|
*
|
|
|
|
|
* @return the CodegenType for this generator
|
|
|
|
|
* @see io.swagger.codegen.CodegenType
|
|
|
|
|
*/
|
|
|
|
|
public CodegenType getTag() {
|
|
|
|
|
return CodegenType.SERVER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
|
public String getName() {
|
|
|
|
|
return "flaskConnexion";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns human-friendly help for the generator. Provide the consumer with help
|
|
|
|
|
* tips, parameters here
|
|
|
|
|
*
|
|
|
|
|
* @return A string value for the help message
|
|
|
|
|
*/
|
|
|
|
|
public String getHelp() {
|
|
|
|
|
return "Generates a python server library using the connexion project. By default, " +
|
|
|
|
|
"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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String toApiFilename(String name) {
|
|
|
|
|
return toApiName(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 reseved words
|
|
|
|
|
*
|
|
|
|
|
* @return the escaped term
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public String escapeReservedWord(String name) {
|
|
|
|
|
return "_" + name; // add an underscore to the name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Location to write api files. You can use the apiPackage() as defined when the class is
|
|
|
|
|
* instantiated
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public String apiFileFolder() {
|
|
|
|
|
return outputFolder + File.separator + apiPackage().replace('.', File.separatorChar);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void preprocessSwagger(Swagger swagger) {
|
|
|
|
|
if(swagger != null && swagger.getPaths() != null) {
|
|
|
|
|
for(String pathname : swagger.getPaths().keySet()) {
|
|
|
|
|
Path path = swagger.getPath(pathname);
|
|
|
|
|
if(path.getOperations() != null) {
|
|
|
|
|
for(Operation operation : path.getOperations()) {
|
|
|
|
|
String operationId = operation.getOperationId();
|
|
|
|
|
if(operationId != null && operationId.indexOf(".") == -1) {
|
|
|
|
|
operation.setVendorExtension("x-operationId", underscore(sanitizeName(operationId)));
|
|
|
|
|
operationId = "controllers.default_controller." + underscore(sanitizeName(operationId));
|
|
|
|
|
operation.setOperationId(operationId);
|
|
|
|
|
}
|
|
|
|
|
if(operation.getTags() != null) {
|
|
|
|
|
List<Map<String, String>> tags = new ArrayList<Map<String, String>>();
|
|
|
|
|
for(String tag : operation.getTags()) {
|
|
|
|
|
Map<String, String> value = new HashMap<String, String>();
|
|
|
|
|
value.put("tag", tag);
|
|
|
|
|
value.put("hasMore", "true");
|
|
|
|
|
tags.add(value);
|
|
|
|
|
}
|
|
|
|
|
if(tags.size() > 0) {
|
|
|
|
|
tags.get(tags.size() - 1).remove("hasMore");
|
|
|
|
|
}
|
|
|
|
|
if(operation.getTags().size() > 0) {
|
|
|
|
|
String tag = operation.getTags().get(0);
|
|
|
|
|
operation.setTags(Arrays.asList(tag));
|
|
|
|
|
}
|
|
|
|
|
operation.setVendorExtension("x-tags", tags);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
String tag = "default_controller";
|
|
|
|
|
operation.setTags(Arrays.asList(tag));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
private List<Map<String, Object>> getOperations(Map<String, Object> objs) {
|
|
|
|
|
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
|
|
|
|
|
Map<String, Object> apiInfo = (Map<String, Object>) objs.get("apiInfo");
|
|
|
|
|
List<Map<String, Object>> apis = (List<Map<String, Object>>) apiInfo.get("apis");
|
|
|
|
|
for (Map<String, Object> api : apis) {
|
|
|
|
|
result.add((Map<String, Object>) api.get("operations"));
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<Map<String, Object>> sortOperationsByPath(List<CodegenOperation> ops) {
|
|
|
|
|
Multimap<String, CodegenOperation> opsByPath = ArrayListMultimap.create();
|
|
|
|
|
|
|
|
|
|
for (CodegenOperation op : ops) {
|
|
|
|
|
opsByPath.put(op.path, op);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<Map<String, Object>> opsByPathList = new ArrayList<Map<String, Object>>();
|
|
|
|
|
for (Map.Entry<String, Collection<CodegenOperation>> entry : opsByPath.asMap().entrySet()) {
|
|
|
|
|
Map<String, Object> opsByPathEntry = new HashMap<String, Object>();
|
|
|
|
|
opsByPathList.add(opsByPathEntry);
|
|
|
|
|
opsByPathEntry.put("path", entry.getKey());
|
|
|
|
|
opsByPathEntry.put("operation", entry.getValue());
|
|
|
|
|
List<CodegenOperation> operationsForThisPath = Lists.newArrayList(entry.getValue());
|
|
|
|
|
operationsForThisPath.get(operationsForThisPath.size() - 1).hasMore = null;
|
|
|
|
|
if (opsByPathList.size() < opsByPath.asMap().size()) {
|
|
|
|
|
opsByPathEntry.put("hasMore", "true");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return opsByPathList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (Map<String, Object> operations : getOperations(objs)) {
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
List<CodegenOperation> ops = (List<CodegenOperation>) operations.get("operation");
|
|
|
|
|
|
|
|
|
|
List<Map<String, Object>> opsByPathList = sortOperationsByPath(ops);
|
|
|
|
|
operations.put("operationsByPath", opsByPathList);
|
|
|
|
|
}
|
|
|
|
|
return super.postProcessSupportingFileData(objs);
|
|
|
|
|
}
|
|
|
|
|
}
|