From 21ce66509ea97c12f02bf34d340960ac0775b0ba Mon Sep 17 00:00:00 2001 From: wing328 Date: Thu, 29 Mar 2018 16:57:16 +0800 Subject: [PATCH] add cpp server generator --- .../languages/CppPistacheServerCodegen.java | 400 ++++++++++++++++++ ...tCodegen.java => CppQt5ClientCodegen.java} | 4 +- .../languages/HaskellHttpClientCodegen.java | 2 +- .../org.openapitools.codegen.CodegenConfig | 8 +- 4 files changed, 408 insertions(+), 6 deletions(-) create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java rename modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/{Qt5CPPClientCodegen.java => CppQt5ClientCodegen.java} (99%) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java new file mode 100644 index 00000000000..295196387d3 --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppPistacheServerCodegen.java @@ -0,0 +1,400 @@ +package org.openapitools.codegen.languages; + + +import java.io.File; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import io.swagger.v3.parser.util.SchemaTypeUtil; +import org.apache.commons.lang3.StringUtils; +import org.openapitools.codegen.*; +import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.*; +import io.swagger.v3.oas.models.media.*; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.parameters.*; +import io.swagger.v3.core.util.Yaml; + +public class CppPistacheServerCodegen extends AbstractCppCodegen { + protected String implFolder = "impl"; + + @Override + public CodegenType getTag() { + return CodegenType.SERVER; + } + + @Override + public String getName() { + return "pistache-server"; + } + + @Override + public String getHelp() { + return "Generates a C++ API server (based on Pistache)"; + } + + public CppPistacheServerCodegen() { + super(); + + apiPackage = "io.swagger.server.api"; + modelPackage = "io.swagger.server.model"; + + modelTemplateFiles.put("model-header.mustache", ".h"); + modelTemplateFiles.put("model-source.mustache", ".cpp"); + + apiTemplateFiles.put("api-header.mustache", ".h"); + apiTemplateFiles.put("api-source.mustache", ".cpp"); + apiTemplateFiles.put("api-impl-header.mustache", ".h"); + apiTemplateFiles.put("api-impl-source.mustache", ".cpp"); + apiTemplateFiles.put("main-api-server.mustache", ".cpp"); + + embeddedTemplateDir = templateDir = "pistache-server"; + + cliOptions.clear(); + + reservedWords = new HashSet<>(); + + supportingFiles.add(new SupportingFile("modelbase-header.mustache", "model", "ModelBase.h")); + supportingFiles.add(new SupportingFile("modelbase-source.mustache", "model", "ModelBase.cpp")); + supportingFiles.add(new SupportingFile("cmake.mustache", "", "CMakeLists.txt")); + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + + languageSpecificPrimitives = new HashSet( + Arrays.asList("int", "char", "bool", "long", "float", "double", "int32_t", "int64_t")); + + typeMapping = new HashMap(); + typeMapping.put("date", "std::string"); + typeMapping.put("DateTime", "std::string"); + typeMapping.put("string", "std::string"); + typeMapping.put("integer", "int32_t"); + typeMapping.put("long", "int64_t"); + typeMapping.put("boolean", "bool"); + typeMapping.put("array", "std::vector"); + typeMapping.put("map", "std::map"); + typeMapping.put("file", "std::string"); + typeMapping.put("object", "Object"); + typeMapping.put("binary", "std::string"); + typeMapping.put("number", "double"); + typeMapping.put("UUID", "std::string"); + + super.importMapping = new HashMap(); + importMapping.put("std::vector", "#include "); + importMapping.put("std::map", "#include "); + importMapping.put("std::string", "#include "); + importMapping.put("Object", "#include \"Object.h\""); + } + + @Override + public void processOpts() { + super.processOpts(); + + additionalProperties.put("modelNamespaceDeclarations", modelPackage.split("\\.")); + additionalProperties.put("modelNamespace", modelPackage.replaceAll("\\.", "::")); + additionalProperties.put("apiNamespaceDeclarations", apiPackage.split("\\.")); + additionalProperties.put("apiNamespace", apiPackage.replaceAll("\\.", "::")); + } + + /** + * 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) { + return "_" + name; // add an underscore to the name + } + + @Override + public String toModelImport(String name) { + if (importMapping.containsKey(name)) { + return importMapping.get(name); + } else { + return "#include \"" + name + ".h\""; + } + } + + @Override + public CodegenModel fromModel(String name, Schema model, Map allDefinitions) { + CodegenModel codegenModel = super.fromModel(name, model, allDefinitions); + + Set oldImports = codegenModel.imports; + codegenModel.imports = new HashSet<>(); + for (String imp : oldImports) { + String newImp = toModelImport(imp); + if (!newImp.isEmpty()) { + codegenModel.imports.add(newImp); + } + } + + return codegenModel; + } + + @Override + public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, + Map definitions, OpenAPI openAPI) { + CodegenOperation op = super.fromOperation(path, httpMethod, operation, definitions, openAPI); + + if (operation.getResponses() != null && !operation.getResponses().isEmpty()) { + ApiResponse apiResponse = findMethodResponse(operation.getResponses()); + + if (apiResponse != null) { + Schema response = getSchemaFromResponse(apiResponse); + if (response != null) { + CodegenProperty cm = fromProperty("response", response); + op.vendorExtensions.put("x-codegen-response", cm); + if (cm.datatype == "HttpContent") { + op.vendorExtensions.put("x-codegen-response-ishttpcontent", true); + } + } + } + } + + String pathForPistache = path.replaceAll("\\{(.*?)}", ":$1"); + op.vendorExtensions.put("x-codegen-pistache-path", pathForPistache); + + return op; + } + + @SuppressWarnings("unchecked") + @Override + public Map postProcessOperations(Map objs) { + Map operations = (Map) objs.get("operations"); + String classname = (String) operations.get("classname"); + operations.put("classnameSnakeUpperCase", DefaultCodegen.underscore(classname).toUpperCase()); + operations.put("classnameSnakeLowerCase", DefaultCodegen.underscore(classname).toLowerCase()); + + List operationList = (List) operations.get("operation"); + for (CodegenOperation op : operationList) { + boolean consumeJson = false; + boolean isParsingSupported = true; + if (op.bodyParam != null) { + if (op.bodyParam.vendorExtensions == null) { + op.bodyParam.vendorExtensions = new HashMap<>(); + } + + op.bodyParam.vendorExtensions.put("x-codegen-pistache-isStringOrDate", op.bodyParam.isString || op.bodyParam.isDate); + } + if(op.consumes != null) { + for (Map consume : op.consumes) { + if (consume.get("mediaType") != null && consume.get("mediaType").equals("application/json")) { + consumeJson = true; + } + } + } + + op.httpMethod = op.httpMethod.substring(0, 1).toUpperCase() + op.httpMethod.substring(1).toLowerCase(); + + for(CodegenParameter param : op.allParams){ + if (param.isFormParam) isParsingSupported=false; + if (param.isFile) isParsingSupported=false; + if (param.isCookieParam) isParsingSupported=false; + + //TODO: This changes the info about the real type but it is needed to parse the header params + if (param.isHeaderParam) { + param.dataType = "Optional"; + param.baseType = "Optional"; + } else if(param.isQueryParam){ + if(param.isPrimitiveType) { + param.dataType = "Optional<" + param.dataType + ">"; + } else { + param.dataType = "Optional<" + param.baseType + ">"; + param.baseType = "Optional<" + param.baseType + ">"; + } + } + } + + if (op.vendorExtensions == null) { + op.vendorExtensions = new HashMap<>(); + } + op.vendorExtensions.put("x-codegen-pistache-consumesJson", consumeJson); + op.vendorExtensions.put("x-codegen-pistache-isParsingSupported", isParsingSupported); + } + + return objs; + } + + @Override + public String toModelFilename(String name) { + return initialCaps(name); + } + + @Override + public String apiFilename(String templateName, String tag) { + String result = super.apiFilename(templateName, tag); + + if ( templateName.endsWith("impl-header.mustache") ) { + int ix = result.lastIndexOf('/'); + result = result.substring(0, ix) + result.substring(ix, result.length() - 2) + "Impl.h"; + result = result.replace(apiFileFolder(), implFileFolder()); + } else if ( templateName.endsWith("impl-source.mustache") ) { + int ix = result.lastIndexOf('/'); + result = result.substring(0, ix) + result.substring(ix, result.length() - 4) + "Impl.cpp"; + result = result.replace(apiFileFolder(), implFileFolder()); + } else if ( templateName.endsWith("api-server.mustache") ) { + int ix = result.lastIndexOf('/'); + result = result.substring(0, ix) + result.substring(ix, result.length() - 4) + "MainServer.cpp"; + result = result.replace(apiFileFolder(), outputFolder); + } + return result; + } + + @Override + public String toApiFilename(String name) { + return initialCaps(name) + "Api"; + } + + /** + * Optional - type declaration. This is a String which is used by the + * templates to instantiate your types. There is typically special handling + * for different property types + * + * @return a string value used as the `dataType` field for model templates, + * `returnType` for api templates + */ + @Override + public String getTypeDeclaration(Schema p) { + String swaggerType = getSchemaType(p); + + if (p instanceof ArraySchema) { + ArraySchema ap = (ArraySchema) p; + Schema inner = ap.getItems(); + return getSchemaType(p) + "<" + getTypeDeclaration(inner) + ">"; + } + if (p instanceof MapSchema) { + MapSchema mp = (MapSchema) p; + Schema inner = (Schema) mp.getAdditionalProperties(); + return getSchemaType(p) + ""; + } + if (p instanceof StringSchema || p instanceof DateSchema + || p instanceof DateTimeSchema || p instanceof FileSchema + || languageSpecificPrimitives.contains(swaggerType)) { + return toModelName(swaggerType); + } + + return "std::shared_ptr<" + swaggerType + ">"; + } + + @Override + public String toDefaultValue(Schema p) { + if (p instanceof StringSchema) { + return "\"\""; + } else if (p instanceof BooleanSchema) { + return "false"; + } else if (p instanceof DateSchema) { + return "\"\""; + } else if (p instanceof DateTimeSchema) { + return "\"\""; + } else if (p instanceof NumberSchema) { + if (SchemaTypeUtil.FLOAT_FORMAT.equals(p.getFormat())) { + return "0.0f"; + } + return "0.0"; + } else if (p instanceof IntegerSchema) { + if (SchemaTypeUtil.INTEGER64_FORMAT.equals(p.getFormat())) { + return "0L"; + } + return "0"; + } else if (p instanceof MapSchema) { + MapSchema ap = (MapSchema) p; + String inner = getSchemaType((Schema) ap.getAdditionalProperties()); + return "std::map()"; + } else if (p instanceof ArraySchema) { + ArraySchema ap = (ArraySchema) p; + String inner = getSchemaType(ap.getItems()); + if (!languageSpecificPrimitives.contains(inner)) { + inner = "std::shared_ptr<" + inner + ">"; + } + return "std::vector<" + inner + ">()"; + } else if (!StringUtils.isEmpty(p.get$ref())) { // model + return "new " + toModelName(p.get$ref()) + "()"; + } + return "nullptr"; + } + + @Override + public void postProcessParameter(CodegenParameter parameter) { + super.postProcessParameter(parameter); + + boolean isPrimitiveType = parameter.isPrimitiveType == Boolean.TRUE; + boolean isListContainer = parameter.isListContainer == Boolean.TRUE; + boolean isString = parameter.isString == Boolean.TRUE; + + if (!isPrimitiveType && !isListContainer && !isString && !parameter.dataType.startsWith("std::shared_ptr")) { + parameter.dataType = "std::shared_ptr<" + parameter.dataType + ">"; + } + } + + /** + * Location to write model files. You can use the modelPackage() as defined + * when the class is instantiated + */ + public String modelFileFolder() { + return (outputFolder + "/model").replace("/", File.separator); + } + + /** + * Location to write api files. You can use the apiPackage() as defined when + * the class is instantiated + */ + @Override + public String apiFileFolder() { + return (outputFolder + "/api").replace("/", File.separator); + } + + private String implFileFolder() { + return (outputFolder + "/" + implFolder).replace("/", File.separator); + } + + /** + * Optional - swagger type conversion. This is used to map swagger types in + * a `Schema` into either language specific types via `typeMapping` or + * into complex models if there is not a mapping. + * + * @return a string value of the type or complex model for this property + */ + @Override + public String getSchemaType(Schema p) { + String swaggerType = super.getSchemaType(p); + String type = null; + if (typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + if (languageSpecificPrimitives.contains(type)) + return toModelName(type); + } else + type = swaggerType; + return toModelName(type); + } + + @Override + public String toModelName(String type) { + if (typeMapping.keySet().contains(type) || typeMapping.values().contains(type) + || importMapping.values().contains(type) || defaultIncludes.contains(type) + || languageSpecificPrimitives.contains(type)) { + return type; + } else { + return Character.toUpperCase(type.charAt(0)) + type.substring(1); + } + } + + @Override + public String toApiName(String type) { + return Character.toUpperCase(type.charAt(0)) + type.substring(1) + "Api"; + } + + @Override + public String escapeQuotationMark(String input) { + // remove " to avoid code injection + return input.replace("\"", ""); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("*/", "*_/").replace("/*", "/_*"); + } +} diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Qt5CPPClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppQt5ClientCodegen.java similarity index 99% rename from modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Qt5CPPClientCodegen.java rename to modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppQt5ClientCodegen.java index e23bc88231f..311b7186d89 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Qt5CPPClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppQt5ClientCodegen.java @@ -15,7 +15,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -public class Qt5CPPClientCodegen extends AbstractCppCodegen implements CodegenConfig { +public class CppQt5ClientCodegen extends AbstractCppCodegen implements CodegenConfig { public static final String CPP_NAMESPACE = "cppNamespace"; public static final String CPP_NAMESPACE_DESC = "C++ namespace (convention: name::space::for::api)."; public static final String OPTIONAL_PROJECT_FILE_DESC = "Generate client.pri."; @@ -30,7 +30,7 @@ public class Qt5CPPClientCodegen extends AbstractCppCodegen implements CodegenCo protected String cppNamespace = "Swagger"; protected boolean optionalProjectFileFlag = true; - public Qt5CPPClientCodegen() { + public CppQt5ClientCodegen() { super(); // set the output folder here diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java index 4729cb23c6a..2eaf33370a3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellHttpClientCodegen.java @@ -4,8 +4,8 @@ import java.util.*; import java.util.regex.Pattern; import java.io.File; -import io.swagger.v3.oas.models.security.SecurityScheme; import org.openapitools.codegen.*; +import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.*; import io.swagger.v3.oas.models.media.*; import io.swagger.v3.oas.models.parameters.*; diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig index e4b3a329016..c7236138571 100644 --- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig +++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -4,7 +4,9 @@ org.openapitools.codegen.languages.AkkaScalaClientCodegen org.openapitools.codegen.languages.BashClientCodegen org.openapitools.codegen.languages.ClojureClientCodegen org.openapitools.codegen.languages.ConfluenceWikiCodegen +org.openapitools.codegen.languages.CppQt5ClientCodegen org.openapitools.codegen.languages.CppRestClientCodegen +org.openapitools.codegen.languages.CppPistacheServerCodegen org.openapitools.codegen.languages.DartClientCodegen org.openapitools.codegen.languages.ElixirClientCodegen org.openapitools.codegen.languages.KotlinClientCodegen @@ -13,14 +15,14 @@ org.openapitools.codegen.languages.HaskellHttpClientCodegen org.openapitools.codegen.languages.HaskellServantCodegen org.openapitools.codegen.languages.JavascriptClientCodegen org.openapitools.codegen.languages.LuaClientCodegen +org.openapitools.codegen.languages.ObjcClientCodegen +org.openapitools.codegen.languages.PerlClientCodegen +org.openapitools.codegen.languages.PhpClientCodegen org.openapitools.codegen.languages.PhpLumenServerCodegen org.openapitools.codegen.languages.PhpSlimServerCodegen org.openapitools.codegen.languages.PhpSilexServerCodegen org.openapitools.codegen.languages.PhpSymfonyServerCodegen org.openapitools.codegen.languages.PhpZendExpressivePathHandlerServerCodegen -org.openapitools.codegen.languages.ObjcClientCodegen -org.openapitools.codegen.languages.PerlClientCodegen -org.openapitools.codegen.languages.PhpClientCodegen org.openapitools.codegen.languages.PowerShellClientCodegen org.openapitools.codegen.languages.PythonClientCodegen org.openapitools.codegen.languages.PythonFlaskConnexionServerCodegen