forked from loafle/openapi-generator-original
add python generator and tests
This commit is contained in:
parent
9cec2b3673
commit
1c22ec363b
@ -0,0 +1,674 @@
|
||||
package org.openapitools.codegen.languages;
|
||||
|
||||
import org.openapitools.codegen.CliOption;
|
||||
import org.openapitools.codegen.CodegenConfig;
|
||||
import org.openapitools.codegen.CodegenConstants;
|
||||
import org.openapitools.codegen.CodegenModel;
|
||||
import org.openapitools.codegen.CodegenParameter;
|
||||
import org.openapitools.codegen.CodegenProperty;
|
||||
import org.openapitools.codegen.CodegenType;
|
||||
import org.openapitools.codegen.DefaultCodegen;
|
||||
import org.openapitools.codegen.SupportingFile;
|
||||
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.Operation;
|
||||
import io.swagger.v3.oas.models.media.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
|
||||
public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
public static final String PACKAGE_URL = "packageUrl";
|
||||
public static final String DEFAULT_LIBRARY = "urllib3";
|
||||
|
||||
protected String packageName; // e.g. petstore_api
|
||||
protected String packageVersion;
|
||||
protected String projectName; // for setup.py, e.g. petstore-api
|
||||
protected String packageUrl;
|
||||
protected String apiDocPath = "docs/";
|
||||
protected String modelDocPath = "docs/";
|
||||
|
||||
protected Map<Character, String> regexModifiers;
|
||||
|
||||
private String testFolder;
|
||||
|
||||
public PythonClientCodegen() {
|
||||
super();
|
||||
|
||||
// clear import mapping (from default generator) as python does not use it
|
||||
// at the moment
|
||||
importMapping.clear();
|
||||
|
||||
supportsInheritance = true;
|
||||
modelPackage = "models";
|
||||
apiPackage = "api";
|
||||
outputFolder = "generated-code" + File.separatorChar + "python";
|
||||
|
||||
modelTemplateFiles.put("model.mustache", ".py");
|
||||
apiTemplateFiles.put("api.mustache", ".py");
|
||||
|
||||
modelTestTemplateFiles.put("model_test.mustache", ".py");
|
||||
apiTestTemplateFiles.put("api_test.mustache", ".py");
|
||||
|
||||
embeddedTemplateDir = templateDir = "python";
|
||||
|
||||
modelDocTemplateFiles.put("model_doc.mustache", ".md");
|
||||
apiDocTemplateFiles.put("api_doc.mustache", ".md");
|
||||
|
||||
testFolder = "test";
|
||||
|
||||
languageSpecificPrimitives.clear();
|
||||
languageSpecificPrimitives.add("int");
|
||||
languageSpecificPrimitives.add("float");
|
||||
languageSpecificPrimitives.add("list");
|
||||
languageSpecificPrimitives.add("dict");
|
||||
languageSpecificPrimitives.add("bool");
|
||||
languageSpecificPrimitives.add("str");
|
||||
languageSpecificPrimitives.add("datetime");
|
||||
languageSpecificPrimitives.add("date");
|
||||
languageSpecificPrimitives.add("object");
|
||||
|
||||
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");
|
||||
// TODO binary should be mapped to byte array
|
||||
// mapped to String as a workaround
|
||||
typeMapping.put("binary", "str");
|
||||
typeMapping.put("ByteArray", "str");
|
||||
// map uuid to string for the time being
|
||||
typeMapping.put("UUID", "str");
|
||||
|
||||
// from https://docs.python.org/3/reference/lexical_analysis.html#keywords
|
||||
setReservedWordsLowerCase(
|
||||
Arrays.asList(
|
||||
// local variable name used in API methods (endpoints)
|
||||
"all_params", "resource_path", "path_params", "query_params",
|
||||
"header_params", "form_params", "local_var_files", "body_params", "auth_settings",
|
||||
// @property
|
||||
"property",
|
||||
// python reserved words
|
||||
"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", "self", "nonlocal", "None", "True", "False"));
|
||||
|
||||
regexModifiers = new HashMap<Character, String>();
|
||||
regexModifiers.put('i', "IGNORECASE");
|
||||
regexModifiers.put('l', "LOCALE");
|
||||
regexModifiers.put('m', "MULTILINE");
|
||||
regexModifiers.put('s', "DOTALL");
|
||||
regexModifiers.put('u', "UNICODE");
|
||||
regexModifiers.put('x', "VERBOSE");
|
||||
|
||||
cliOptions.clear();
|
||||
cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "python package name (convention: snake_case).")
|
||||
.defaultValue("swagger_client"));
|
||||
cliOptions.add(new CliOption(CodegenConstants.PROJECT_NAME, "python project name in setup.py (e.g. petstore-api)."));
|
||||
cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, "python package version.")
|
||||
.defaultValue("1.0.0"));
|
||||
cliOptions.add(new CliOption(PACKAGE_URL, "python package URL."));
|
||||
cliOptions.add(CliOption.newBoolean(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG,
|
||||
CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG_DESC).defaultValue(Boolean.TRUE.toString()));
|
||||
cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "hides the timestamp when files were generated")
|
||||
.defaultValue(Boolean.TRUE.toString()));
|
||||
|
||||
supportedLibraries.put("urllib3", "urllib3-based client");
|
||||
supportedLibraries.put("asyncio", "Asyncio-based client (python 3.5+)");
|
||||
supportedLibraries.put("tornado", "tornado-based client");
|
||||
CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use");
|
||||
libraryOption.setDefault(DEFAULT_LIBRARY);
|
||||
cliOptions.add(libraryOption);
|
||||
setLibrary(DEFAULT_LIBRARY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processOpts() {
|
||||
super.processOpts();
|
||||
Boolean excludeTests = false;
|
||||
|
||||
if(additionalProperties.containsKey(CodegenConstants.EXCLUDE_TESTS)) {
|
||||
excludeTests = Boolean.valueOf(additionalProperties.get(CodegenConstants.EXCLUDE_TESTS).toString());
|
||||
}
|
||||
|
||||
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) {
|
||||
setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME));
|
||||
}
|
||||
else {
|
||||
setPackageName("swagger_client");
|
||||
}
|
||||
|
||||
if (additionalProperties.containsKey(CodegenConstants.PROJECT_NAME)) {
|
||||
setProjectName((String) additionalProperties.get(CodegenConstants.PROJECT_NAME));
|
||||
}
|
||||
else {
|
||||
// default: set project based on package name
|
||||
// e.g. petstore_api (package name) => petstore-api (project name)
|
||||
setProjectName(packageName.replaceAll("_", "-"));
|
||||
}
|
||||
|
||||
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_VERSION)) {
|
||||
setPackageVersion((String) additionalProperties.get(CodegenConstants.PACKAGE_VERSION));
|
||||
}
|
||||
else {
|
||||
setPackageVersion("1.0.0");
|
||||
}
|
||||
|
||||
// default HIDE_GENERATION_TIMESTAMP to true
|
||||
if (!additionalProperties.containsKey(CodegenConstants.HIDE_GENERATION_TIMESTAMP)) {
|
||||
additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, Boolean.TRUE.toString());
|
||||
} else {
|
||||
additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP,
|
||||
Boolean.valueOf(additionalProperties().get(CodegenConstants.HIDE_GENERATION_TIMESTAMP).toString()));
|
||||
}
|
||||
|
||||
additionalProperties.put(CodegenConstants.PROJECT_NAME, projectName);
|
||||
additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName);
|
||||
additionalProperties.put(CodegenConstants.PACKAGE_VERSION, packageVersion);
|
||||
|
||||
// make api and model doc path available in mustache template
|
||||
additionalProperties.put("apiDocPath", apiDocPath);
|
||||
additionalProperties.put("modelDocPath", modelDocPath);
|
||||
|
||||
if (additionalProperties.containsKey(PACKAGE_URL)) {
|
||||
setPackageUrl((String) additionalProperties.get(PACKAGE_URL));
|
||||
}
|
||||
|
||||
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
|
||||
|
||||
supportingFiles.add(new SupportingFile("tox.mustache", "", "tox.ini"));
|
||||
supportingFiles.add(new SupportingFile("test-requirements.mustache", "", "test-requirements.txt"));
|
||||
supportingFiles.add(new SupportingFile("requirements.mustache", "", "requirements.txt"));
|
||||
|
||||
supportingFiles.add(new SupportingFile("configuration.mustache", packageName, "configuration.py"));
|
||||
supportingFiles.add(new SupportingFile("__init__package.mustache", packageName, "__init__.py"));
|
||||
supportingFiles.add(new SupportingFile("__init__model.mustache", packageName + File.separatorChar + modelPackage, "__init__.py"));
|
||||
supportingFiles.add(new SupportingFile("__init__api.mustache", packageName + File.separatorChar + apiPackage, "__init__.py"));
|
||||
|
||||
if(Boolean.FALSE.equals(excludeTests)) {
|
||||
supportingFiles.add(new SupportingFile("__init__test.mustache", testFolder, "__init__.py"));
|
||||
}
|
||||
supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh"));
|
||||
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
|
||||
supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml"));
|
||||
supportingFiles.add(new SupportingFile("setup.mustache", "", "setup.py"));
|
||||
supportingFiles.add(new SupportingFile("api_client.mustache", packageName, "api_client.py"));
|
||||
|
||||
if ("asyncio".equals(getLibrary())) {
|
||||
supportingFiles.add(new SupportingFile("asyncio/rest.mustache", packageName, "rest.py"));
|
||||
additionalProperties.put("asyncio", "true");
|
||||
} else if ("tornado".equals(getLibrary())) {
|
||||
supportingFiles.add(new SupportingFile("tornado/rest.mustache", packageName, "rest.py"));
|
||||
additionalProperties.put("tornado", "true");
|
||||
} else {
|
||||
supportingFiles.add(new SupportingFile("rest.mustache", packageName, "rest.py"));
|
||||
}
|
||||
|
||||
modelPackage = packageName + "." + modelPackage;
|
||||
apiPackage = packageName + "." + apiPackage;
|
||||
|
||||
}
|
||||
|
||||
private static String dropDots(String str) {
|
||||
return str.replaceAll("\\.", "_");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toModelImport(String name) {
|
||||
String modelImport;
|
||||
if (StringUtils.startsWithAny(name,"import", "from")) {
|
||||
modelImport = name;
|
||||
} else {
|
||||
modelImport = "from ";
|
||||
if (!"".equals(modelPackage())) {
|
||||
modelImport += modelPackage() + ".";
|
||||
}
|
||||
modelImport += toModelFilename(name)+ " import " + name;
|
||||
}
|
||||
return modelImport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
|
||||
// process enum in models
|
||||
return postProcessModelsEnum(objs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessParameter(CodegenParameter parameter){
|
||||
postProcessPattern(parameter.pattern, parameter.vendorExtensions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
|
||||
postProcessPattern(property.pattern, property.vendorExtensions);
|
||||
}
|
||||
|
||||
/*
|
||||
* The swagger pattern spec follows the Perl convention and style of modifiers. Python
|
||||
* does not support this in as natural a way so it needs to convert it. See
|
||||
* https://docs.python.org/2/howto/regex.html#compilation-flags for details.
|
||||
*/
|
||||
public void postProcessPattern(String pattern, Map<String, Object> vendorExtensions){
|
||||
if(pattern != null) {
|
||||
int i = pattern.lastIndexOf('/');
|
||||
|
||||
//Must follow Perl /pattern/modifiers convention
|
||||
if(pattern.charAt(0) != '/' || i < 2) {
|
||||
throw new IllegalArgumentException("Pattern must follow the Perl "
|
||||
+ "/pattern/modifiers convention. "+pattern+" is not valid.");
|
||||
}
|
||||
|
||||
String regex = pattern.substring(1, i).replace("'", "\\'");
|
||||
List<String> modifiers = new ArrayList<String>();
|
||||
|
||||
for(char c : pattern.substring(i).toCharArray()) {
|
||||
if(regexModifiers.containsKey(c)) {
|
||||
String modifier = regexModifiers.get(c);
|
||||
modifiers.add(modifier);
|
||||
}
|
||||
}
|
||||
|
||||
vendorExtensions.put("x-regex", regex);
|
||||
vendorExtensions.put("x-modifiers", modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodegenType getTag() {
|
||||
return CodegenType.CLIENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "python";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelp() {
|
||||
return "Generates a Python client library.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String escapeReservedWord(String name) {
|
||||
if(this.reservedWordsMappings().containsKey(name)) {
|
||||
return this.reservedWordsMappings().get(name);
|
||||
}
|
||||
return "_" + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apiDocFileFolder() {
|
||||
return (outputFolder + "/" + apiDocPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String modelDocFileFolder() {
|
||||
return (outputFolder + "/" + modelDocPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toModelDocFilename(String name) {
|
||||
return toModelName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toApiDocFilename(String name) {
|
||||
return toApiName(name);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String apiFileFolder() {
|
||||
return outputFolder + File.separatorChar + apiPackage().replace('.', File.separatorChar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String modelFileFolder() {
|
||||
return outputFolder + File.separatorChar + modelPackage().replace('.', File.separatorChar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apiTestFileFolder() {
|
||||
return outputFolder + File.separatorChar + testFolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String modelTestFileFolder() {
|
||||
return outputFolder + File.separatorChar + testFolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeDeclaration(Schema p) {
|
||||
if (p instanceof ArraySchema) {
|
||||
ArraySchema ap = (ArraySchema) p;
|
||||
Schema inner = ap.getItems();
|
||||
return getSchemaType(p) + "[" + getTypeDeclaration(inner) + "]";
|
||||
} else if (p instanceof MapSchema) {
|
||||
MapSchema mp = (MapSchema) p;
|
||||
Schema inner = (Schema) mp.getAdditionalProperties();
|
||||
|
||||
return getSchemaType(p) + "(str, " + getTypeDeclaration(inner) + ")";
|
||||
}
|
||||
return super.getTypeDeclaration(p);
|
||||
}
|
||||
|
||||
@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 type;
|
||||
}
|
||||
} else {
|
||||
type = toModelName(swaggerType);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toVarName(String name) {
|
||||
// sanitize name
|
||||
name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
|
||||
|
||||
// remove dollar sign
|
||||
name = name.replaceAll("$", "");
|
||||
|
||||
// if it's all uppper case, convert to lower case
|
||||
if (name.matches("^[A-Z_]*$")) {
|
||||
name = name.toLowerCase();
|
||||
}
|
||||
|
||||
// underscore the variable name
|
||||
// petId => pet_id
|
||||
name = underscore(name);
|
||||
|
||||
// remove leading underscore
|
||||
name = name.replaceAll("^_*", "");
|
||||
|
||||
// for reserved word or word starting with number, append _
|
||||
if (isReservedWord(name) || name.matches("^\\d.*")) {
|
||||
name = escapeReservedWord(name);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toParamName(String name) {
|
||||
// to avoid conflicts with 'callback' parameter for async call
|
||||
if ("callback".equals(name)) {
|
||||
return "param_callback";
|
||||
}
|
||||
|
||||
// should be the same as variable name
|
||||
return toVarName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toModelName(String name) {
|
||||
name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
|
||||
// remove dollar sign
|
||||
name = name.replaceAll("$", "");
|
||||
|
||||
// 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));
|
||||
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 " + camelize("model_" + name));
|
||||
name = "model_" + name; // e.g. 200Response => Model200Response (after camelize)
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(modelNamePrefix)) {
|
||||
name = modelNamePrefix + "_" + name;
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(modelNameSuffix)) {
|
||||
name = name + "_" + modelNameSuffix;
|
||||
}
|
||||
|
||||
// camelize the model name
|
||||
// phone_number => PhoneNumber
|
||||
return camelize(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toModelFilename(String name) {
|
||||
// underscore the model file name
|
||||
// PhoneNumber => phone_number
|
||||
return underscore(dropDots(toModelName(name)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toModelTestFilename(String name) {
|
||||
return "test_" + toModelFilename(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toApiFilename(String name) {
|
||||
// replace - with _ e.g. created-at => created_at
|
||||
name = name.replaceAll("-", "_");
|
||||
|
||||
// e.g. PhoneNumberApi.py => phone_number_api.py
|
||||
return underscore(name) + "_api";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toApiTestFilename(String name) {
|
||||
return "test_" + toApiFilename(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toApiName(String name) {
|
||||
if (name.length() == 0) {
|
||||
return "DefaultApi";
|
||||
}
|
||||
// e.g. phone_number_api => PhoneNumberApi
|
||||
return camelize(name) + "Api";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toApiVarName(String name) {
|
||||
if (name.length() == 0) {
|
||||
return "default_api";
|
||||
}
|
||||
return underscore(name) + "_api";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toOperationId(String operationId) {
|
||||
// throw exception if method name is empty (should not occur as an auto-generated method name will be used)
|
||||
if (StringUtils.isEmpty(operationId)) {
|
||||
throw new RuntimeException("Empty method name (operationId) not allowed");
|
||||
}
|
||||
|
||||
// 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 " + underscore(sanitizeName("call_" + operationId)));
|
||||
operationId = "call_" + operationId;
|
||||
}
|
||||
|
||||
return underscore(sanitizeName(operationId));
|
||||
}
|
||||
|
||||
public void setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
public void setProjectName(String projectName) {
|
||||
this.projectName= projectName;
|
||||
}
|
||||
|
||||
public void setPackageVersion(String packageVersion) {
|
||||
this.packageVersion = packageVersion;
|
||||
}
|
||||
|
||||
public void setPackageUrl(String packageUrl) {
|
||||
this.packageUrl = packageUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Python package name from String `packageName`
|
||||
*
|
||||
* (PEP 0008) Python packages should also have short, all-lowercase names,
|
||||
* although the use of underscores is discouraged.
|
||||
*
|
||||
* @param packageName Package name
|
||||
* @return Python package name that conforms to PEP 0008
|
||||
*/
|
||||
@SuppressWarnings("static-method")
|
||||
public String generatePackageName(String packageName) {
|
||||
return underscore(packageName.replaceAll("[^\\w]+", ""));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default value of the property
|
||||
*
|
||||
* @param p Swagger property object
|
||||
* @return string presentation of the default value of the property
|
||||
*/
|
||||
@Override
|
||||
public String toDefaultValue(Schema p) {
|
||||
if (p instanceof StringSchema) {
|
||||
StringSchema dp = (StringSchema) p;
|
||||
if (dp.getDefault() != null) {
|
||||
if (Pattern.compile("\r\n|\r|\n").matcher(dp.getDefault()).find())
|
||||
return "'''" + dp.getDefault() + "'''";
|
||||
else
|
||||
return "'" + dp.getDefault() + "'";
|
||||
}
|
||||
} else if (p instanceof BooleanSchema) {
|
||||
BooleanSchema dp = (BooleanSchema) p;
|
||||
if (dp.getDefault() != null) {
|
||||
if (dp.getDefault().toString().equalsIgnoreCase("false"))
|
||||
return "False";
|
||||
else
|
||||
return "True";
|
||||
}
|
||||
} else if (p instanceof DateSchema) {
|
||||
// TODO
|
||||
} else if (p instanceof DateTimeSchema) {
|
||||
// TODO
|
||||
} else if (p instanceof NumberSchema) {
|
||||
NumberSchema dp = (NumberSchema) p;
|
||||
if (dp.getDefault() != null) {
|
||||
return dp.getDefault().toString();
|
||||
}
|
||||
} else if (p instanceof IntegerSchema) {
|
||||
IntegerSchema dp = (IntegerSchema) p;
|
||||
if (dp.getDefault() != null) {
|
||||
return dp.getDefault().toString();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameterExampleValue(CodegenParameter p) {
|
||||
String example;
|
||||
|
||||
if (p.defaultValue == null) {
|
||||
example = p.example;
|
||||
} else {
|
||||
example = p.defaultValue;
|
||||
}
|
||||
|
||||
String type = p.baseType;
|
||||
if (type == null) {
|
||||
type = p.dataType;
|
||||
}
|
||||
|
||||
if ("String".equalsIgnoreCase(type) || "str".equalsIgnoreCase(type)) {
|
||||
if (example == null) {
|
||||
example = p.paramName + "_example";
|
||||
}
|
||||
example = "'" + escapeText(example) + "'";
|
||||
} else if ("Integer".equals(type) || "int".equals(type)) {
|
||||
if (example == null) {
|
||||
example = "56";
|
||||
}
|
||||
} else if ("Float".equalsIgnoreCase(type) || "Double".equalsIgnoreCase(type)) {
|
||||
if (example == null) {
|
||||
example = "3.4";
|
||||
}
|
||||
} else if ("BOOLEAN".equalsIgnoreCase(type) || "bool".equalsIgnoreCase(type)) {
|
||||
if (example == null) {
|
||||
example = "True";
|
||||
}
|
||||
} else if ("file".equalsIgnoreCase(type)) {
|
||||
if (example == null) {
|
||||
example = "/path/to/file";
|
||||
}
|
||||
example = "'" + escapeText(example) + "'";
|
||||
} else if ("Date".equalsIgnoreCase(type)) {
|
||||
if (example == null) {
|
||||
example = "2013-10-20";
|
||||
}
|
||||
example = "'" + escapeText(example) + "'";
|
||||
} else if ("DateTime".equalsIgnoreCase(type)) {
|
||||
if (example == null) {
|
||||
example = "2013-10-20T19:20:30+01:00";
|
||||
}
|
||||
example = "'" + escapeText(example) + "'";
|
||||
} else if (!languageSpecificPrimitives.contains(type)) {
|
||||
// type is a model class, e.g. User
|
||||
example = this.packageName + "." + type + "()";
|
||||
} else {
|
||||
LOGGER.warn("Type " + type + " not handled properly in setParameterExampleValue");
|
||||
}
|
||||
|
||||
if (example == null) {
|
||||
example = "NULL";
|
||||
} else if (Boolean.TRUE.equals(p.isListContainer)) {
|
||||
example = "[" + example + "]";
|
||||
} else if (Boolean.TRUE.equals(p.isMapContainer)) {
|
||||
example = "{'key': " + example + "}";
|
||||
}
|
||||
|
||||
p.example = example;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sanitizeTag(String tag) {
|
||||
return sanitizeName(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String escapeQuotationMark(String input) {
|
||||
// remove ' to avoid code injection
|
||||
return input.replace("'", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String escapeUnsafeCharacters(String input) {
|
||||
// remove multiline comment
|
||||
return input.replace("'''", "'_'_'");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package org.openapitools.codegen.python;
|
||||
|
||||
import org.openapitools.codegen.AbstractOptionsTest;
|
||||
import org.openapitools.codegen.CodegenConfig;
|
||||
import org.openapitools.codegen.languages.PythonClientCodegen;
|
||||
import org.openapitools.codegen.options.PythonClientOptionsProvider;
|
||||
|
||||
import mockit.Expectations;
|
||||
import mockit.Tested;
|
||||
|
||||
public class PythonClientOptionsTest extends AbstractOptionsTest {
|
||||
|
||||
@Tested
|
||||
private PythonClientCodegen clientCodegen;
|
||||
|
||||
public PythonClientOptionsTest() {
|
||||
super(new PythonClientOptionsProvider());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CodegenConfig getCodegenConfig() {
|
||||
return clientCodegen;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
protected void setExpectations() {
|
||||
new Expectations(clientCodegen) {{
|
||||
clientCodegen.setPackageName(PythonClientOptionsProvider.PACKAGE_NAME_VALUE);
|
||||
clientCodegen.setProjectName(PythonClientOptionsProvider.PROJECT_NAME_VALUE);
|
||||
clientCodegen.setPackageVersion(PythonClientOptionsProvider.PACKAGE_VERSION_VALUE);
|
||||
clientCodegen.setPackageUrl(PythonClientOptionsProvider.PACKAGE_URL_VALUE);
|
||||
// clientCodegen.setLibrary(PythonClientCodegen.DEFAULT_LIBRARY);
|
||||
times = 1;
|
||||
}};
|
||||
}
|
||||
}
|
@ -0,0 +1,275 @@
|
||||
package org.openapitools.codegen.python;
|
||||
|
||||
import org.openapitools.codegen.CodegenModel;
|
||||
import org.openapitools.codegen.CodegenOperation;
|
||||
import org.openapitools.codegen.CodegenProperty;
|
||||
import org.openapitools.codegen.DefaultCodegen;
|
||||
import org.openapitools.codegen.languages.PythonClientCodegen;
|
||||
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.Operation;
|
||||
import io.swagger.v3.oas.models.media.*;
|
||||
import io.swagger.v3.parser.OpenAPIV3Parser;
|
||||
import io.swagger.v3.parser.util.SchemaTypeUtil;
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
import org.testng.annotations.ITestAnnotation;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("static-method")
|
||||
public class PythonTest {
|
||||
|
||||
@Test(description = "convert a python model with dots")
|
||||
public void modelTest() {
|
||||
final OpenAPI openAPI= new OpenAPIV3Parser().read("src/test/resources/2_0/v1beta3.json");
|
||||
final DefaultCodegen codegen = new PythonClientCodegen();
|
||||
|
||||
final CodegenModel simpleName = codegen.fromModel("v1beta3.Binding", openAPI.getComponents().getSchemas().get("v1beta3.Binding"));
|
||||
Assert.assertEquals(simpleName.name, "v1beta3.Binding");
|
||||
Assert.assertEquals(simpleName.classname, "V1beta3Binding");
|
||||
Assert.assertEquals(simpleName.classVarName, "v1beta3_binding");
|
||||
|
||||
final CodegenModel compoundName = codegen.fromModel("v1beta3.ComponentStatus", openAPI.getComponents().getSchemas().get("v1beta3.ComponentStatus"));
|
||||
Assert.assertEquals(compoundName.name, "v1beta3.ComponentStatus");
|
||||
Assert.assertEquals(compoundName.classname, "V1beta3ComponentStatus");
|
||||
Assert.assertEquals(compoundName.classVarName, "v1beta3_component_status");
|
||||
|
||||
final String path = "/api/v1beta3/namespaces/{namespaces}/bindings";
|
||||
final Operation operation = openAPI.getPaths().get(path).getPost();
|
||||
final CodegenOperation codegenOperation = codegen.fromOperation(path, "get", operation, openAPI.getComponents().getSchemas());
|
||||
Assert.assertEquals(codegenOperation.returnType, "V1beta3Binding");
|
||||
Assert.assertEquals(codegenOperation.returnBaseType, "V1beta3Binding");
|
||||
}
|
||||
|
||||
@Test(description = "convert a simple java model")
|
||||
public void simpleModelTest() {
|
||||
final Schema schema = new Schema()
|
||||
.description("a sample model")
|
||||
.addProperties("id", new IntegerSchema().format(SchemaTypeUtil.INTEGER64_FORMAT))
|
||||
.addProperties("name", new StringSchema())
|
||||
.addProperties("createdAt", new DateTimeSchema())
|
||||
.addRequiredItem("id")
|
||||
.addRequiredItem("name");
|
||||
final DefaultCodegen codegen = new PythonClientCodegen();
|
||||
final CodegenModel cm = codegen.fromModel("sample", schema);
|
||||
|
||||
Assert.assertEquals(cm.name, "sample");
|
||||
Assert.assertEquals(cm.classname, "Sample");
|
||||
Assert.assertEquals(cm.description, "a sample model");
|
||||
Assert.assertEquals(cm.vars.size(), 3);
|
||||
|
||||
final CodegenProperty property1 = cm.vars.get(0);
|
||||
Assert.assertEquals(property1.baseName, "id");
|
||||
Assert.assertEquals(property1.datatype, "int");
|
||||
Assert.assertEquals(property1.name, "id");
|
||||
Assert.assertNull(property1.defaultValue);
|
||||
Assert.assertEquals(property1.baseType, "int");
|
||||
Assert.assertTrue(property1.hasMore);
|
||||
Assert.assertTrue(property1.required);
|
||||
Assert.assertTrue(property1.isPrimitiveType);
|
||||
Assert.assertTrue(property1.isNotContainer);
|
||||
|
||||
final CodegenProperty property2 = cm.vars.get(1);
|
||||
Assert.assertEquals(property2.baseName, "name");
|
||||
Assert.assertEquals(property2.datatype, "str");
|
||||
Assert.assertEquals(property2.name, "name");
|
||||
Assert.assertNull(property2.defaultValue);
|
||||
Assert.assertEquals(property2.baseType, "str");
|
||||
Assert.assertTrue(property2.hasMore);
|
||||
Assert.assertTrue(property2.required);
|
||||
Assert.assertTrue(property2.isPrimitiveType);
|
||||
Assert.assertTrue(property2.isNotContainer);
|
||||
|
||||
final CodegenProperty property3 = cm.vars.get(2);
|
||||
Assert.assertEquals(property3.baseName, "createdAt");
|
||||
Assert.assertEquals(property3.datatype, "datetime");
|
||||
Assert.assertEquals(property3.name, "created_at");
|
||||
Assert.assertNull(property3.defaultValue);
|
||||
Assert.assertEquals(property3.baseType, "datetime");
|
||||
Assert.assertFalse(property3.hasMore);
|
||||
Assert.assertFalse(property3.required);
|
||||
Assert.assertTrue(property3.isNotContainer);
|
||||
}
|
||||
|
||||
@Test(description = "convert a model with list property")
|
||||
public void listPropertyTest() {
|
||||
final Schema model = new Schema()
|
||||
.description("a sample model")
|
||||
.addProperties("id", new IntegerSchema().format(SchemaTypeUtil.INTEGER64_FORMAT))
|
||||
.addProperties("urls", new ArraySchema()
|
||||
.items(new StringSchema()))
|
||||
.addRequiredItem("id");
|
||||
final DefaultCodegen codegen = new PythonClientCodegen();
|
||||
final CodegenModel cm = codegen.fromModel("sample", model);
|
||||
|
||||
Assert.assertEquals(cm.name, "sample");
|
||||
Assert.assertEquals(cm.classname, "Sample");
|
||||
Assert.assertEquals(cm.description, "a sample model");
|
||||
Assert.assertEquals(cm.vars.size(), 2);
|
||||
|
||||
final CodegenProperty property1 = cm.vars.get(0);
|
||||
Assert.assertEquals(property1.baseName, "id");
|
||||
Assert.assertEquals(property1.datatype, "int");
|
||||
Assert.assertEquals(property1.name, "id");
|
||||
Assert.assertNull(property1.defaultValue);
|
||||
Assert.assertEquals(property1.baseType, "int");
|
||||
Assert.assertTrue(property1.hasMore);
|
||||
Assert.assertTrue(property1.required);
|
||||
Assert.assertTrue(property1.isPrimitiveType);
|
||||
Assert.assertTrue(property1.isNotContainer);
|
||||
|
||||
final CodegenProperty property2 = cm.vars.get(1);
|
||||
Assert.assertEquals(property2.baseName, "urls");
|
||||
Assert.assertEquals(property2.datatype, "list[str]");
|
||||
Assert.assertEquals(property2.name, "urls");
|
||||
Assert.assertNull(property2.defaultValue);
|
||||
Assert.assertEquals(property2.baseType, "list");
|
||||
Assert.assertFalse(property2.hasMore);
|
||||
Assert.assertEquals(property2.containerType, "array");
|
||||
Assert.assertFalse(property2.required);
|
||||
Assert.assertTrue(property2.isPrimitiveType);
|
||||
Assert.assertTrue(property2.isContainer);
|
||||
}
|
||||
|
||||
@Test(description = "convert a model with a map property")
|
||||
public void mapPropertyTest() {
|
||||
final Schema model = new Schema()
|
||||
.description("a sample model")
|
||||
.addProperties("translations", new MapSchema()
|
||||
.additionalProperties(new StringSchema()))
|
||||
.addRequiredItem("id");
|
||||
final DefaultCodegen codegen = new PythonClientCodegen();
|
||||
final CodegenModel cm = codegen.fromModel("sample", model);
|
||||
|
||||
Assert.assertEquals(cm.name, "sample");
|
||||
Assert.assertEquals(cm.classname, "Sample");
|
||||
Assert.assertEquals(cm.description, "a sample model");
|
||||
Assert.assertEquals(cm.vars.size(), 1);
|
||||
|
||||
final CodegenProperty property1 = cm.vars.get(0);
|
||||
Assert.assertEquals(property1.baseName, "translations");
|
||||
Assert.assertEquals(property1.datatype, "dict(str, str)");
|
||||
Assert.assertEquals(property1.name, "translations");
|
||||
Assert.assertEquals(property1.baseType, "dict");
|
||||
Assert.assertEquals(property1.containerType, "map");
|
||||
Assert.assertFalse(property1.required);
|
||||
Assert.assertTrue(property1.isContainer);
|
||||
Assert.assertTrue(property1.isPrimitiveType);
|
||||
}
|
||||
|
||||
@Test(description = "convert a model with complex property")
|
||||
public void complexPropertyTest() {
|
||||
final Schema model = new Schema()
|
||||
.description("a sample model")
|
||||
.addProperties("children", new Schema().$ref("#/definitions/Children"));
|
||||
final DefaultCodegen codegen = new PythonClientCodegen();
|
||||
final CodegenModel cm = codegen.fromModel("sample", model);
|
||||
|
||||
Assert.assertEquals(cm.name, "sample");
|
||||
Assert.assertEquals(cm.classname, "Sample");
|
||||
Assert.assertEquals(cm.description, "a sample model");
|
||||
Assert.assertEquals(cm.vars.size(), 1);
|
||||
|
||||
final CodegenProperty property1 = cm.vars.get(0);
|
||||
Assert.assertEquals(property1.baseName, "children");
|
||||
Assert.assertEquals(property1.datatype, "Children");
|
||||
Assert.assertEquals(property1.name, "children");
|
||||
Assert.assertEquals(property1.baseType, "Children");
|
||||
Assert.assertFalse(property1.required);
|
||||
Assert.assertTrue(property1.isNotContainer);
|
||||
}
|
||||
|
||||
@Test(description = "convert a model with complex list property")
|
||||
public void complexListPropertyTest() {
|
||||
final Schema model = new Schema()
|
||||
.description("a sample model")
|
||||
.addProperties("children", new ArraySchema()
|
||||
.items(new Schema().$ref("#/definitions/Children")));
|
||||
final DefaultCodegen codegen = new PythonClientCodegen();
|
||||
final CodegenModel cm = codegen.fromModel("sample", model);
|
||||
|
||||
Assert.assertEquals(cm.name, "sample");
|
||||
Assert.assertEquals(cm.classname, "Sample");
|
||||
Assert.assertEquals(cm.description, "a sample model");
|
||||
Assert.assertEquals(cm.vars.size(), 1);
|
||||
|
||||
final CodegenProperty property1 = cm.vars.get(0);
|
||||
Assert.assertEquals(property1.baseName, "children");
|
||||
Assert.assertEquals(property1.complexType, "Children");
|
||||
Assert.assertEquals(property1.datatype, "list[Children]");
|
||||
Assert.assertEquals(property1.name, "children");
|
||||
Assert.assertEquals(property1.baseType, "list");
|
||||
Assert.assertEquals(property1.containerType, "array");
|
||||
Assert.assertFalse(property1.required);
|
||||
Assert.assertTrue(property1.isContainer);
|
||||
}
|
||||
|
||||
@Test(description = "convert a model with complex map property")
|
||||
public void complexMapPropertyTest() {
|
||||
final Schema model = new Schema()
|
||||
.description("a sample model")
|
||||
.addProperties("children", new MapSchema()
|
||||
.additionalProperties(new Schema().$ref("#/definitions/Children")));
|
||||
final DefaultCodegen codegen = new PythonClientCodegen();
|
||||
final CodegenModel cm = codegen.fromModel("sample", model);
|
||||
|
||||
Assert.assertEquals(cm.name, "sample");
|
||||
Assert.assertEquals(cm.classname, "Sample");
|
||||
Assert.assertEquals(cm.description, "a sample model");
|
||||
Assert.assertEquals(cm.vars.size(), 1);
|
||||
Assert.assertEquals(Sets.intersection(cm.imports, Sets.newHashSet("Children")).size(), 1);
|
||||
|
||||
final CodegenProperty property1 = cm.vars.get(0);
|
||||
Assert.assertEquals(property1.baseName, "children");
|
||||
Assert.assertEquals(property1.complexType, "Children");
|
||||
Assert.assertEquals(property1.datatype, "dict(str, Children)");
|
||||
Assert.assertEquals(property1.name, "children");
|
||||
Assert.assertEquals(property1.baseType, "dict");
|
||||
Assert.assertEquals(property1.containerType, "map");
|
||||
Assert.assertFalse(property1.required);
|
||||
Assert.assertTrue(property1.isContainer);
|
||||
Assert.assertFalse(property1.isNotContainer);
|
||||
}
|
||||
|
||||
|
||||
// should not start with 'null'. need help from the community to investigate further
|
||||
@Test(enabled = false, description = "convert an array model")
|
||||
public void arrayModelTest() {
|
||||
final Schema model = new ArraySchema()
|
||||
//.description()
|
||||
.items(new Schema().$ref("#/definitions/Children"))
|
||||
.description("an array model");
|
||||
final DefaultCodegen codegen = new PythonClientCodegen();
|
||||
final CodegenModel cm = codegen.fromModel("sample", model);
|
||||
|
||||
Assert.assertEquals(cm.name, "sample");
|
||||
Assert.assertEquals(cm.classname, "Sample");
|
||||
Assert.assertEquals(cm.description, "an array model");
|
||||
Assert.assertEquals(cm.vars.size(), 0);
|
||||
Assert.assertEquals(cm.parent, "null<Children>");
|
||||
Assert.assertEquals(cm.imports.size(), 1);
|
||||
Assert.assertEquals(Sets.intersection(cm.imports, Sets.newHashSet("Children")).size(), 1);
|
||||
}
|
||||
|
||||
// should not start with 'null'. need help from the community to investigate further
|
||||
@Test(enabled = false, description = "convert an map model")
|
||||
public void mapModelTest() {
|
||||
final Schema model = new Schema()
|
||||
.description("a map model")
|
||||
.additionalProperties(new Schema().$ref("#/definitions/Children"));
|
||||
final DefaultCodegen codegen = new PythonClientCodegen();
|
||||
final CodegenModel cm = codegen.fromModel("sample", model);
|
||||
|
||||
Assert.assertEquals(cm.name, "sample");
|
||||
Assert.assertEquals(cm.classname, "Sample");
|
||||
Assert.assertEquals(cm.description, "a map model");
|
||||
Assert.assertEquals(cm.vars.size(), 0);
|
||||
Assert.assertEquals(cm.parent, "null<String, Children>");
|
||||
Assert.assertEquals(cm.imports.size(), 2); // TODO: need to verify
|
||||
Assert.assertEquals(Sets.intersection(cm.imports, Sets.newHashSet("Children")).size(), 1);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package org.openapitools.codegen.options;
|
||||
|
||||
import org.openapitools.codegen.CodegenConstants;
|
||||
import org.openapitools.codegen.languages.PythonClientCodegen;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class PythonClientOptionsProvider implements OptionsProvider {
|
||||
public static final String PACKAGE_NAME_VALUE = "swagger_client_python";
|
||||
public static final String PROJECT_NAME_VALUE = "swagger-client-python";
|
||||
public static final String PACKAGE_VERSION_VALUE = "1.0.0-SNAPSHOT";
|
||||
public static final String PACKAGE_URL_VALUE = "";
|
||||
|
||||
@Override
|
||||
public String getLanguage() {
|
||||
return "python";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> createOptions() {
|
||||
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<String, String>();
|
||||
return builder.put(PythonClientCodegen.PACKAGE_URL, PACKAGE_URL_VALUE)
|
||||
.put(CodegenConstants.PACKAGE_NAME, PACKAGE_NAME_VALUE)
|
||||
.put(CodegenConstants.PROJECT_NAME, PROJECT_NAME_VALUE)
|
||||
.put(CodegenConstants.PACKAGE_VERSION, PACKAGE_VERSION_VALUE)
|
||||
.put(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, "true")
|
||||
.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "true")
|
||||
.put(CodegenConstants.LIBRARY, "urllib3")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isServer() {
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user