From a224c608ddd402ace587fac741f5a5effa6bb256 Mon Sep 17 00:00:00 2001 From: Julien Sagnard Date: Fri, 16 Nov 2018 10:17:13 +0100 Subject: [PATCH 1/4] Astract factory for generators based on connexion --- .../PythonAbstractConnexionServerCodegen.java | 722 ++++++++++++++++++ .../PythonFlaskConnexionServerCodegen.java | 691 +---------------- 2 files changed, 724 insertions(+), 689 deletions(-) create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAbstractConnexionServerCodegen.java diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAbstractConnexionServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAbstractConnexionServerCodegen.java new file mode 100644 index 000000000000..adf151f6aa51 --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAbstractConnexionServerCodegen.java @@ -0,0 +1,722 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.languages; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.PathItem.HttpMethod; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.io.FilenameUtils; +import org.openapitools.codegen.*; +import org.openapitools.codegen.utils.ModelUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.File; +import java.util.*; + +import static org.openapitools.codegen.utils.StringUtils.camelize; +import static org.openapitools.codegen.utils.StringUtils.underscore; + +public class PythonAbstractConnexionServerCodegen extends DefaultCodegen implements CodegenConfig { + private static final Logger LOGGER = LoggerFactory.getLogger(PythonAbstractConnexionServerCodegen.class); + + public static final String CONTROLLER_PACKAGE = "controllerPackage"; + public static final String DEFAULT_CONTROLLER = "defaultController"; + public static final String SUPPORT_PYTHON2 = "supportPython2"; + + protected int serverPort = 8080; + protected String packageName; + protected String packageVersion; + protected String controllerPackage; + protected String defaultController; + protected Map regexModifiers; + + public PythonAbstractConnexionServerCodegen(String templateDirectory) { + super(); + modelPackage = "models"; + testPackage = "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("file"); + 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"); + typeMapping.put("UUID", "str"); + + // from https://docs.python.org/3/reference/lexical_analysis.html#keywords + setReservedWordsLowerCase( + Arrays.asList( + // @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", "None", "True", "False", "nonlocal")); + + // set the output folder here + outputFolder = "generated-code/connexion"; + + apiTemplateFiles.put("controller.mustache", ".py"); + modelTemplateFiles.put("model.mustache", ".py"); + apiTestTemplateFiles().put("controller_test.mustache", ".py"); + + /* + * 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 = templateDirectory; + + /* + * Additional Properties. These values can be passed to the templates and + * are available in models, apis, and supporting files + */ + 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("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("setup.mustache", "", "setup.py")); + 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("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("Dockerfile.mustache", "", "Dockerfile")); + supportingFiles.add(new SupportingFile("dockerignore.mustache", "", ".dockerignore")); + + regexModifiers = new HashMap(); + 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.add(new CliOption(CodegenConstants.PACKAGE_NAME, "python package name (convention: snake_case).") + .defaultValue("openapi_server")); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, "python package version.") + .defaultValue("1.0.0")); + cliOptions.add(new CliOption(CONTROLLER_PACKAGE, "controller package"). + defaultValue("controllers")); + cliOptions.add(new CliOption(DEFAULT_CONTROLLER, "default controller"). + defaultValue("default_controller")); + cliOptions.add(new CliOption(SUPPORT_PYTHON2, "support python2"). + defaultValue("false")); + cliOptions.add(new CliOption("serverPort", "TCP port to listen to in app.run"). + defaultValue("8080")); + } + + @Override + public void processOpts() { + super.processOpts(); + + if (StringUtils.isEmpty(System.getenv("PYTHON_POST_PROCESS_FILE"))) { + LOGGER.info("Environment variable PYTHON_POST_PROCESS_FILE not defined so the Python code may not be properly formatted. To define it, try 'export PYTHON_POST_PROCESS_FILE=\"/usr/local/bin/yapf -i\"' (Linux/Mac)"); + LOGGER.info("NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI)."); + } + + //apiTemplateFiles.clear(); + + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { + setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME)); + } else { + setPackageName("openapi_server"); + additionalProperties.put(CodegenConstants.PACKAGE_NAME, this.packageName); + } + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_VERSION)) { + setPackageVersion((String) additionalProperties.get(CodegenConstants.PACKAGE_VERSION)); + } else { + setPackageVersion("1.0.0"); + additionalProperties.put(CodegenConstants.PACKAGE_VERSION, this.packageVersion); + } + if (additionalProperties.containsKey(CONTROLLER_PACKAGE)) { + this.controllerPackage = additionalProperties.get(CONTROLLER_PACKAGE).toString(); + } else { + this.controllerPackage = "controllers"; + additionalProperties.put(CONTROLLER_PACKAGE, this.controllerPackage); + } + if (additionalProperties.containsKey(DEFAULT_CONTROLLER)) { + this.defaultController = additionalProperties.get(DEFAULT_CONTROLLER).toString(); + } else { + this.defaultController = "default_controller"; + additionalProperties.put(DEFAULT_CONTROLLER, this.defaultController); + } + if (Boolean.TRUE.equals(additionalProperties.get(SUPPORT_PYTHON2))) { + additionalProperties.put(SUPPORT_PYTHON2, Boolean.TRUE); + typeMapping.put("long", "long"); + } + supportingFiles.add(new SupportingFile("__init__.mustache", packageName, "__init__.py")); + supportingFiles.add(new SupportingFile("__main__.mustache", packageName, "__main__.py")); + supportingFiles.add(new SupportingFile("encoder.mustache", packageName, "encoder.py")); + supportingFiles.add(new SupportingFile("util.mustache", packageName, "util.py")); + supportingFiles.add(new SupportingFile("__init__.mustache", packageName + File.separatorChar + controllerPackage, "__init__.py")); + supportingFiles.add(new SupportingFile("__init__model.mustache", packageName + File.separatorChar + modelPackage, "__init__.py")); + supportingFiles.add(new SupportingFile("base_model_.mustache", packageName + File.separatorChar + modelPackage, "base_model_.py")); + supportingFiles.add(new SupportingFile("__init__test.mustache", packageName + File.separatorChar + testPackage, "__init__.py")); + supportingFiles.add(new SupportingFile("openapi.mustache", packageName + File.separatorChar + "openapi", "openapi.yaml")); + + modelPackage = packageName + "." + modelPackage; + controllerPackage = packageName + "." + controllerPackage; + testPackage = packageName + "." + testPackage; + } + + private static String dropDots(String str) { + return str.replaceAll("\\.", "_"); + } + + @Override + public String apiPackage() { + return controllerPackage; + } + + /** + * Configures the type of generator. + * + * @return the CodegenType for this generator + * @see org.openapitools.codegen.CodegenType + */ + @Override + public CodegenType getTag() { + return CodegenType.SERVER; + } + + /** + * 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 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 == null || name.length() == 0) { + return "DefaultController"; + } + return camelize(name, false) + "Controller"; + } + + @Override + public String toApiFilename(String name) { + return underscore(toApiName(name)); + } + + @Override + public String toApiTestFilename(String name) { + return "test_" + toApiFilename(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 + * + * @return the escaped term + */ + @Override + public String escapeReservedWord(String name) { + if (this.reservedWordsMappings().containsKey(name)) { + return this.reservedWordsMappings().get(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 String getTypeDeclaration(Schema p) { + if (ModelUtils.isArraySchema(p)) { + ArraySchema ap = (ArraySchema) p; + Schema inner = ap.getItems(); + return getSchemaType(p) + "[" + getTypeDeclaration(inner) + "]"; + } else if (ModelUtils.isMapSchema(p)) { + Schema inner = ModelUtils.getAdditionalProperties(p); + return getSchemaType(p) + "[str, " + getTypeDeclaration(inner) + "]"; + } + return super.getTypeDeclaration(p); + } + + @Override + public String getSchemaType(Schema p) { + String schemaType = super.getSchemaType(p); + String type = null; + if (typeMapping.containsKey(schemaType)) { + type = typeMapping.get(schemaType); + if (languageSpecificPrimitives.contains(type)) { + return type; + } + } else { + type = toModelName(schemaType); + } + return type; + } + + @Override + public void preprocessOpenAPI(OpenAPI openAPI) { + // need vendor extensions for x-openapi-router-controller + Map paths = openAPI.getPaths(); + if (paths != null) { + for (String pathname : paths.keySet()) { + PathItem path = paths.get(pathname); + Map operationMap = path.readOperationsMap(); + if (operationMap != null) { + for (HttpMethod method : operationMap.keySet()) { + Operation operation = operationMap.get(method); + String tag = "default"; + if (operation.getTags() != null && operation.getTags().size() > 0) { + tag = operation.getTags().get(0); + } + String operationId = operation.getOperationId(); + if (operationId == null) { + operationId = getOrGenerateOperationId(operation, pathname, method.toString()); + } + operation.setOperationId(toOperationId(operationId)); + if (operation.getExtensions() == null || operation.getExtensions().get("x-openapi-router-controller") == null) { + operation.addExtension( + "x-openapi-router-controller", + controllerPackage + "." + toApiFilename(tag) + ); + } + } + } + } + } + } + + @SuppressWarnings("unchecked") + private static List> getOperations(Map objs) { + List> result = new ArrayList>(); + Map apiInfo = (Map) objs.get("apiInfo"); + List> apis = (List>) apiInfo.get("apis"); + for (Map api : apis) { + result.add((Map) api.get("operations")); + } + return result; + } + + private static List> sortOperationsByPath(List ops) { + Multimap opsByPath = ArrayListMultimap.create(); + + for (CodegenOperation op : ops) { + opsByPath.put(op.path, op); + } + + List> opsByPathList = new ArrayList>(); + for (Map.Entry> entry : opsByPath.asMap().entrySet()) { + Map opsByPathEntry = new HashMap(); + opsByPathList.add(opsByPathEntry); + opsByPathEntry.put("path", entry.getKey()); + opsByPathEntry.put("operation", entry.getValue()); + List operationsForThisPath = Lists.newArrayList(entry.getValue()); + operationsForThisPath.get(operationsForThisPath.size() - 1).hasMore = false; + if (opsByPathList.size() < opsByPath.asMap().size()) { + opsByPathEntry.put("hasMore", "true"); + } + } + + return opsByPathList; + } + + @Override + public Map postProcessSupportingFileData(Map objs) { + generateYAMLSpecFile(objs); + + for (Map operations : getOperations(objs)) { + @SuppressWarnings("unchecked") + List ops = (List) operations.get("operation"); + + List> opsByPathList = sortOperationsByPath(ops); + operations.put("operationsByPath", opsByPathList); + } + return super.postProcessSupportingFileData(objs); + } + + @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(Locale.ROOT); + } + + // 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 toModelFilename(String name) { + // underscore the model file name + // PhoneNumber => phone_number + return underscore(dropDots(toModelName(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 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)); + } + + /** + * Return the default value of the property + * + * @param p OpenAPI property object + * @return string presentation of the default value of the property + */ + @Override + public String toDefaultValue(Schema p) { + if (ModelUtils.isBooleanSchema(p)) { + if (p.getDefault() != null) { + if (p.getDefault().toString().equalsIgnoreCase("false")) + return "False"; + else + return "True"; + } + } else if (ModelUtils.isDateSchema(p)) { + // TODO + } else if (ModelUtils.isDateTimeSchema(p)) { + // TODO + } else if (ModelUtils.isNumberSchema(p)) { + if (p.getDefault() != null) { + return p.getDefault().toString(); + } + } else if (ModelUtils.isIntegerSchema(p)) { + if (p.getDefault() != null) { + return p.getDefault().toString(); + } + } else if (ModelUtils.isStringSchema(p)) { + if (p.getDefault() != null) { + return "'" + (String) p.getDefault() + "'"; + } + } + + return null; + } + + @Override + public void setParameterExampleValue(CodegenParameter p) { + String example; + + if (p.defaultValue == null) { + example = p.example; + } else { + p.example = p.defaultValue; + return; + } + + 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 (p.minimum != null) { + example = "" + (Integer.valueOf(p.minimum) + 1); + } + if (p.maximum != null) { + example = "" + p.maximum; + } else if (example == null) { + example = "56"; + } + + } else if ("Long".equalsIgnoreCase(type)) { + if (p.minimum != null) { + example = "" + (Long.valueOf(p.minimum) + 1); + } + if (p.maximum != null) { + example = "" + p.maximum; + } else if (example == null) { + example = "789"; + } + } else if ("Float".equalsIgnoreCase(type) || "Double".equalsIgnoreCase(type)) { + if (p.minimum != null) { + example = "" + p.minimum; + } else if (p.maximum != null) { + example = "" + p.maximum; + } else if (example == null) { + example = "3.4"; + } + } else if ("BOOLEAN".equalsIgnoreCase(type) || "bool".equalsIgnoreCase(type)) { + if (example == null) { + example = "True"; + } + } else if ("file".equalsIgnoreCase(type)) { + example = "(BytesIO(b'some file data'), 'file.txt')"; + } 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 = type + "()"; + } else { + LOGGER.warn("Type " + type + " not handled properly in setParameterExampleValue"); + } + + if (p.items != null && p.items.defaultValue != null) { + example = p.items.defaultValue; + } + if (example == null) { + example = "None"; + } else if (Boolean.TRUE.equals(p.isListContainer)) { + if (Boolean.TRUE.equals(p.isBodyParam)) { + example = "[" + example + "]"; + } + } else if (Boolean.TRUE.equals(p.isMapContainer)) { + example = "{'key': " + example + "}"; + } + + p.example = example; + } + + 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) { + // remove multiline comment + return input.replace("'''", "'_'_'"); + } + + @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 void postProcessModelProperty(CodegenModel model, CodegenProperty property) { + if (StringUtils.isNotEmpty(property.pattern)) { + addImport(model, "import re"); + } + postProcessPattern(property.pattern, property.vendorExtensions); + } + + @Override + public Map postProcessModels(Map objs) { + // process enum in models + return postProcessModelsEnum(objs); + } + + @Override + public void postProcessParameter(CodegenParameter parameter) { + postProcessPattern(parameter.pattern, parameter.vendorExtensions); + } + + /* + * The openapi 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 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 modifiers = new ArrayList(); + + 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 void postProcessFile(File file, String fileType) { + if (file == null) { + return; + } + String pythonPostProcessFile = System.getenv("PYTHON_POST_PROCESS_FILE"); + if (StringUtils.isEmpty(pythonPostProcessFile)) { + return; // skip if PYTHON_POST_PROCESS_FILE env variable is not defined + } + + // only process files with py extension + if ("py".equals(FilenameUtils.getExtension(file.toString()))) { + String command = pythonPostProcessFile + " " + file.toString(); + try { + Process p = Runtime.getRuntime().exec(command); + int exitValue = p.waitFor(); + if (exitValue != 0) { + LOGGER.error("Error running the command ({}). Exit value: {}", command, exitValue); + } else { + LOGGER.info("Successfully executed: " + command); + } + } catch (Exception e) { + LOGGER.error("Error running the command ({}). Exception: {}", command, e.getMessage()); + } + } + } + +} diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFlaskConnexionServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFlaskConnexionServerCodegen.java index 498f8abe955b..d83b1bec2dac 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFlaskConnexionServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFlaskConnexionServerCodegen.java @@ -1,6 +1,5 @@ /* * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) - * Copyright 2018 SmartBear Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,214 +16,18 @@ package org.openapitools.codegen.languages; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.PathItem; -import io.swagger.v3.oas.models.PathItem.HttpMethod; -import io.swagger.v3.oas.models.media.ArraySchema; -import io.swagger.v3.oas.models.media.Schema; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.io.FilenameUtils; -import org.openapitools.codegen.*; -import org.openapitools.codegen.utils.ModelUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.util.*; import static org.openapitools.codegen.utils.StringUtils.camelize; import static org.openapitools.codegen.utils.StringUtils.underscore; -public class PythonFlaskConnexionServerCodegen extends DefaultCodegen implements CodegenConfig { +public class PythonFlaskConnexionServerCodegen extends PythonAbstractConnexionServerCodegen { private static final Logger LOGGER = LoggerFactory.getLogger(PythonFlaskConnexionServerCodegen.class); - public static final String CONTROLLER_PACKAGE = "controllerPackage"; - public static final String DEFAULT_CONTROLLER = "defaultController"; - public static final String SUPPORT_PYTHON2 = "supportPython2"; - - protected int serverPort = 8080; - protected String packageName; - protected String packageVersion; - protected String controllerPackage; - protected String defaultController; - protected Map regexModifiers; - public PythonFlaskConnexionServerCodegen() { - super(); - modelPackage = "models"; - testPackage = "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("file"); - 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"); - typeMapping.put("UUID", "str"); - - // from https://docs.python.org/3/reference/lexical_analysis.html#keywords - setReservedWordsLowerCase( - Arrays.asList( - // @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", "None", "True", "False", "nonlocal")); - - // set the output folder here - outputFolder = "generated-code/connexion"; - - apiTemplateFiles.put("controller.mustache", ".py"); - modelTemplateFiles.put("model.mustache", ".py"); - apiTestTemplateFiles().put("controller_test.mustache", ".py"); - - /* - * 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"; - - /* - * Additional Properties. These values can be passed to the templates and - * are available in models, apis, and supporting files - */ - 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("README.mustache", "", "README.md")); - supportingFiles.add(new SupportingFile("setup.mustache", "", "setup.py")); - 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("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("Dockerfile.mustache", "", "Dockerfile")); - supportingFiles.add(new SupportingFile("dockerignore.mustache", "", ".dockerignore")); - - regexModifiers = new HashMap(); - 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.add(new CliOption(CodegenConstants.PACKAGE_NAME, "python package name (convention: snake_case).") - .defaultValue("openapi_server")); - cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, "python package version.") - .defaultValue("1.0.0")); - cliOptions.add(new CliOption(CONTROLLER_PACKAGE, "controller package"). - defaultValue("controllers")); - cliOptions.add(new CliOption(DEFAULT_CONTROLLER, "default controller"). - defaultValue("default_controller")); - cliOptions.add(new CliOption(SUPPORT_PYTHON2, "support python2"). - defaultValue("false")); - cliOptions.add(new CliOption("serverPort", "TCP port to listen to in app.run"). - defaultValue("8080")); - } - - @Override - public void processOpts() { - super.processOpts(); - - if (StringUtils.isEmpty(System.getenv("PYTHON_POST_PROCESS_FILE"))) { - LOGGER.info("Environment variable PYTHON_POST_PROCESS_FILE not defined so the Python code may not be properly formatted. To define it, try 'export PYTHON_POST_PROCESS_FILE=\"/usr/local/bin/yapf -i\"' (Linux/Mac)"); - LOGGER.info("NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI)."); - } - - //apiTemplateFiles.clear(); - - if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { - setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME)); - } else { - setPackageName("openapi_server"); - additionalProperties.put(CodegenConstants.PACKAGE_NAME, this.packageName); - } - if (additionalProperties.containsKey(CodegenConstants.PACKAGE_VERSION)) { - setPackageVersion((String) additionalProperties.get(CodegenConstants.PACKAGE_VERSION)); - } else { - setPackageVersion("1.0.0"); - additionalProperties.put(CodegenConstants.PACKAGE_VERSION, this.packageVersion); - } - if (additionalProperties.containsKey(CONTROLLER_PACKAGE)) { - this.controllerPackage = additionalProperties.get(CONTROLLER_PACKAGE).toString(); - } else { - this.controllerPackage = "controllers"; - additionalProperties.put(CONTROLLER_PACKAGE, this.controllerPackage); - } - if (additionalProperties.containsKey(DEFAULT_CONTROLLER)) { - this.defaultController = additionalProperties.get(DEFAULT_CONTROLLER).toString(); - } else { - this.defaultController = "default_controller"; - additionalProperties.put(DEFAULT_CONTROLLER, this.defaultController); - } - if (Boolean.TRUE.equals(additionalProperties.get(SUPPORT_PYTHON2))) { - additionalProperties.put(SUPPORT_PYTHON2, Boolean.TRUE); - typeMapping.put("long", "long"); - } - supportingFiles.add(new SupportingFile("__init__.mustache", packageName, "__init__.py")); - supportingFiles.add(new SupportingFile("__main__.mustache", packageName, "__main__.py")); - supportingFiles.add(new SupportingFile("encoder.mustache", packageName, "encoder.py")); - supportingFiles.add(new SupportingFile("util.mustache", packageName, "util.py")); - supportingFiles.add(new SupportingFile("__init__.mustache", packageName + File.separatorChar + controllerPackage, "__init__.py")); - supportingFiles.add(new SupportingFile("__init__model.mustache", packageName + File.separatorChar + modelPackage, "__init__.py")); - supportingFiles.add(new SupportingFile("base_model_.mustache", packageName + File.separatorChar + modelPackage, "base_model_.py")); - supportingFiles.add(new SupportingFile("__init__test.mustache", packageName + File.separatorChar + testPackage, "__init__.py")); - supportingFiles.add(new SupportingFile("openapi.mustache", packageName + File.separatorChar + "openapi", "openapi.yaml")); - - modelPackage = packageName + "." + modelPackage; - controllerPackage = packageName + "." + controllerPackage; - testPackage = packageName + "." + testPackage; - } - - private static String dropDots(String str) { - return str.replaceAll("\\.", "_"); - } - - @Override - public String apiPackage() { - return controllerPackage; - } - - /** - * Configures the type of generator. - * - * @return the CodegenType for this generator - * @see org.openapitools.codegen.CodegenType - */ - @Override - public CodegenType getTag() { - return CodegenType.SERVER; + super("flaskConnexion"); } /** @@ -237,494 +40,4 @@ public CodegenType getTag() { public String getName() { return "python-flask"; } - - /** - * 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 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 == null || name.length() == 0) { - return "DefaultController"; - } - return camelize(name, false) + "Controller"; - } - - @Override - public String toApiFilename(String name) { - return underscore(toApiName(name)); - } - - @Override - public String toApiTestFilename(String name) { - return "test_" + toApiFilename(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 - * - * @return the escaped term - */ - @Override - public String escapeReservedWord(String name) { - if (this.reservedWordsMappings().containsKey(name)) { - return this.reservedWordsMappings().get(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 String getTypeDeclaration(Schema p) { - if (ModelUtils.isArraySchema(p)) { - ArraySchema ap = (ArraySchema) p; - Schema inner = ap.getItems(); - return getSchemaType(p) + "[" + getTypeDeclaration(inner) + "]"; - } else if (ModelUtils.isMapSchema(p)) { - Schema inner = ModelUtils.getAdditionalProperties(p); - return getSchemaType(p) + "[str, " + getTypeDeclaration(inner) + "]"; - } - return super.getTypeDeclaration(p); - } - - @Override - public String getSchemaType(Schema p) { - String schemaType = super.getSchemaType(p); - String type = null; - if (typeMapping.containsKey(schemaType)) { - type = typeMapping.get(schemaType); - if (languageSpecificPrimitives.contains(type)) { - return type; - } - } else { - type = toModelName(schemaType); - } - return type; - } - - @Override - public void preprocessOpenAPI(OpenAPI openAPI) { - // need vendor extensions for x-openapi-router-controller - Map paths = openAPI.getPaths(); - if (paths != null) { - for (String pathname : paths.keySet()) { - PathItem path = paths.get(pathname); - Map operationMap = path.readOperationsMap(); - if (operationMap != null) { - for (HttpMethod method : operationMap.keySet()) { - Operation operation = operationMap.get(method); - String tag = "default"; - if (operation.getTags() != null && operation.getTags().size() > 0) { - tag = operation.getTags().get(0); - } - String operationId = operation.getOperationId(); - if (operationId == null) { - operationId = getOrGenerateOperationId(operation, pathname, method.toString()); - } - operation.setOperationId(toOperationId(operationId)); - if (operation.getExtensions() == null || operation.getExtensions().get("x-openapi-router-controller") == null) { - operation.addExtension( - "x-openapi-router-controller", - controllerPackage + "." + toApiFilename(tag) - ); - } - } - } - } - } - } - - @SuppressWarnings("unchecked") - private static List> getOperations(Map objs) { - List> result = new ArrayList>(); - Map apiInfo = (Map) objs.get("apiInfo"); - List> apis = (List>) apiInfo.get("apis"); - for (Map api : apis) { - result.add((Map) api.get("operations")); - } - return result; - } - - private static List> sortOperationsByPath(List ops) { - Multimap opsByPath = ArrayListMultimap.create(); - - for (CodegenOperation op : ops) { - opsByPath.put(op.path, op); - } - - List> opsByPathList = new ArrayList>(); - for (Map.Entry> entry : opsByPath.asMap().entrySet()) { - Map opsByPathEntry = new HashMap(); - opsByPathList.add(opsByPathEntry); - opsByPathEntry.put("path", entry.getKey()); - opsByPathEntry.put("operation", entry.getValue()); - List operationsForThisPath = Lists.newArrayList(entry.getValue()); - operationsForThisPath.get(operationsForThisPath.size() - 1).hasMore = false; - if (opsByPathList.size() < opsByPath.asMap().size()) { - opsByPathEntry.put("hasMore", "true"); - } - } - - return opsByPathList; - } - - @Override - public Map postProcessSupportingFileData(Map objs) { - generateYAMLSpecFile(objs); - - for (Map operations : getOperations(objs)) { - @SuppressWarnings("unchecked") - List ops = (List) operations.get("operation"); - - List> opsByPathList = sortOperationsByPath(ops); - operations.put("operationsByPath", opsByPathList); - } - return super.postProcessSupportingFileData(objs); - } - - @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(Locale.ROOT); - } - - // 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 toModelFilename(String name) { - // underscore the model file name - // PhoneNumber => phone_number - return underscore(dropDots(toModelName(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 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)); - } - - /** - * Return the default value of the property - * - * @param p OpenAPI property object - * @return string presentation of the default value of the property - */ - @Override - public String toDefaultValue(Schema p) { - if (ModelUtils.isBooleanSchema(p)) { - if (p.getDefault() != null) { - if (p.getDefault().toString().equalsIgnoreCase("false")) - return "False"; - else - return "True"; - } - } else if (ModelUtils.isDateSchema(p)) { - // TODO - } else if (ModelUtils.isDateTimeSchema(p)) { - // TODO - } else if (ModelUtils.isNumberSchema(p)) { - if (p.getDefault() != null) { - return p.getDefault().toString(); - } - } else if (ModelUtils.isIntegerSchema(p)) { - if (p.getDefault() != null) { - return p.getDefault().toString(); - } - } else if (ModelUtils.isStringSchema(p)) { - if (p.getDefault() != null) { - return "'" + (String) p.getDefault() + "'"; - } - } - - return null; - } - - @Override - public void setParameterExampleValue(CodegenParameter p) { - String example; - - if (p.defaultValue == null) { - example = p.example; - } else { - p.example = p.defaultValue; - return; - } - - 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 (p.minimum != null) { - example = "" + (Integer.valueOf(p.minimum) + 1); - } - if (p.maximum != null) { - example = "" + p.maximum; - } else if (example == null) { - example = "56"; - } - - } else if ("Long".equalsIgnoreCase(type)) { - if (p.minimum != null) { - example = "" + (Long.valueOf(p.minimum) + 1); - } - if (p.maximum != null) { - example = "" + p.maximum; - } else if (example == null) { - example = "789"; - } - } else if ("Float".equalsIgnoreCase(type) || "Double".equalsIgnoreCase(type)) { - if (p.minimum != null) { - example = "" + p.minimum; - } else if (p.maximum != null) { - example = "" + p.maximum; - } else if (example == null) { - example = "3.4"; - } - } else if ("BOOLEAN".equalsIgnoreCase(type) || "bool".equalsIgnoreCase(type)) { - if (example == null) { - example = "True"; - } - } else if ("file".equalsIgnoreCase(type)) { - example = "(BytesIO(b'some file data'), 'file.txt')"; - } 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 = type + "()"; - } else { - LOGGER.warn("Type " + type + " not handled properly in setParameterExampleValue"); - } - - if (p.items != null && p.items.defaultValue != null) { - example = p.items.defaultValue; - } - if (example == null) { - example = "None"; - } else if (Boolean.TRUE.equals(p.isListContainer)) { - if (Boolean.TRUE.equals(p.isBodyParam)) { - example = "[" + example + "]"; - } - } else if (Boolean.TRUE.equals(p.isMapContainer)) { - example = "{'key': " + example + "}"; - } - - p.example = example; - } - - 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) { - // remove multiline comment - return input.replace("'''", "'_'_'"); - } - - @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 void postProcessModelProperty(CodegenModel model, CodegenProperty property) { - if (StringUtils.isNotEmpty(property.pattern)) { - addImport(model, "import re"); - } - postProcessPattern(property.pattern, property.vendorExtensions); - } - - @Override - public Map postProcessModels(Map objs) { - // process enum in models - return postProcessModelsEnum(objs); - } - - @Override - public void postProcessParameter(CodegenParameter parameter) { - postProcessPattern(parameter.pattern, parameter.vendorExtensions); - } - - /* - * The openapi 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 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 modifiers = new ArrayList(); - - 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 void postProcessFile(File file, String fileType) { - if (file == null) { - return; - } - String pythonPostProcessFile = System.getenv("PYTHON_POST_PROCESS_FILE"); - if (StringUtils.isEmpty(pythonPostProcessFile)) { - return; // skip if PYTHON_POST_PROCESS_FILE env variable is not defined - } - - // only process files with py extension - if ("py".equals(FilenameUtils.getExtension(file.toString()))) { - String command = pythonPostProcessFile + " " + file.toString(); - try { - Process p = Runtime.getRuntime().exec(command); - int exitValue = p.waitFor(); - if (exitValue != 0) { - LOGGER.error("Error running the command ({}). Exit value: {}", command, exitValue); - } else { - LOGGER.info("Successfully executed: " + command); - } - } catch (Exception e) { - LOGGER.error("Error running the command ({}). Exception: {}", command, e.getMessage()); - } - } - } - } From 2671b5bb024142125234e8322fb8aad06d2046e2 Mon Sep 17 00:00:00 2001 From: Julien Sagnard Date: Wed, 14 Nov 2018 15:40:02 +0100 Subject: [PATCH 2/4] Add aiohttp server generator --- bin/python-aiohttp-petstore.sh | 32 + bin/python-flask-petstore-python2.sh | 1 - bin/python-flask-petstore.sh | 1 - .../openapitools/codegen/CodegenSecurity.java | 3 + .../openapitools/codegen/DefaultCodegen.java | 8 + .../PythonAbstractConnexionServerCodegen.java | 216 ++++- .../PythonAiohttpConnexionServerCodegen.java | 49 ++ .../PythonFlaskConnexionServerCodegen.java | 19 +- .../org.openapitools.codegen.CodegenConfig | 1 + .../flaskConnexion/controller_test.mustache | 22 +- .../resources/flaskConnexion/model.mustache | 7 + .../flaskConnexion/requirements.mustache | 2 +- .../security_controller_.mustache | 43 + .../resources/flaskConnexion/tox.mustache | 2 +- .../resources/python-aiohttp/README.mustache | 46 + .../python-aiohttp/__init__.mustache | 0 .../python-aiohttp/__init__main.mustache | 12 + .../python-aiohttp/__init__model.mustache | 5 + .../python-aiohttp/__init__test.mustache | 0 .../python-aiohttp/__main__.mustache | 6 + .../python-aiohttp/base_model_.mustache | 66 ++ .../python-aiohttp/conftest.mustache | 17 + .../python-aiohttp/controller.mustache | 104 +++ .../python-aiohttp/controller_test.mustache | 68 ++ .../resources/python-aiohttp/model.mustache | 162 ++++ .../resources/python-aiohttp/openapi.mustache | 1 + .../python-aiohttp/param_type.mustache | 1 + .../python-aiohttp/requirements.mustache | 3 + .../security_controller_.mustache | 59 ++ .../python-aiohttp/test-requirements.mustache | 6 + .../resources/python-aiohttp/util.mustache | 130 +++ pom.xml | 3 + .../petstore/flaskConnexion-python2/Makefile | 20 + .../controllers/security_controller_.py | 26 + .../openapi_server/models/pet.py | 2 + .../openapi_server/openapi/openapi.yaml | 21 +- .../test/test_pet_controller.py | 97 ++- .../test/test_store_controller.py | 34 +- .../test/test_user_controller.py | 58 +- .../petstore/flaskConnexion-python2/pom.xml | 46 + .../flaskConnexion-python2/requirements.txt | 2 +- .../flaskConnexion-python2/test_python2.sh | 32 + .../petstore/flaskConnexion-python2/tox.ini | 2 +- .../server/petstore/flaskConnexion/Makefile | 20 + .../controllers/security_controller_.py | 26 + .../openapi_server/models/pet.py | 2 + .../openapi_server/openapi/openapi.yaml | 21 +- .../test/test_pet_controller.py | 97 ++- .../test/test_store_controller.py | 34 +- .../test/test_user_controller.py | 58 +- .../server/petstore/flaskConnexion/pom.xml | 46 + .../petstore/flaskConnexion/requirements.txt | 2 +- .../petstore/flaskConnexion/test_python3.sh | 32 + .../server/petstore/flaskConnexion/tox.ini | 2 +- .../python-aiohttp/.openapi-generator-ignore | 23 + .../python-aiohttp/.openapi-generator/VERSION | 1 + .../server/petstore/python-aiohttp/Makefile | 18 + .../server/petstore/python-aiohttp/README.md | 46 + .../python-aiohttp/openapi_server/__main__.py | 6 + .../openapi_server/controllers/__init__.py | 0 .../controllers/pet_controller.py | 114 +++ .../controllers/security_controller_.py | 29 + .../controllers/store_controller.py | 52 ++ .../controllers/user_controller.py | 107 +++ .../openapi_server/models/__init__.py | 9 + .../openapi_server/models/api_response.py | 110 +++ .../openapi_server/models/base_model_.py | 66 ++ .../openapi_server/models/category.py | 85 ++ .../openapi_server/models/order.py | 193 +++++ .../openapi_server/models/pet.py | 199 +++++ .../openapi_server/models/tag.py | 85 ++ .../openapi_server/models/user.py | 237 ++++++ .../openapi_server/openapi/openapi.yaml | 792 ++++++++++++++++++ .../python-aiohttp/openapi_server/util.py | 130 +++ .../server/petstore/python-aiohttp/pom.xml | 46 + .../petstore/python-aiohttp/requirements.txt | 3 + .../python-aiohttp/test-requirements.txt | 6 + .../petstore/python-aiohttp/test_python3.sh | 32 + .../petstore/python-aiohttp/tests/__init__.py | 0 .../petstore/python-aiohttp/tests/conftest.py | 17 + .../tests/test_pet_controller.py | 200 +++++ .../tests/test_store_controller.py | 76 ++ .../tests/test_user_controller.py | 149 ++++ 83 files changed, 4494 insertions(+), 112 deletions(-) create mode 100755 bin/python-aiohttp-petstore.sh create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAiohttpConnexionServerCodegen.java create mode 100644 modules/openapi-generator/src/main/resources/flaskConnexion/security_controller_.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/README.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/__init__.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/__init__main.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/__init__model.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/__init__test.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/__main__.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/base_model_.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/conftest.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/controller.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/controller_test.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/model.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/openapi.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/param_type.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/security_controller_.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/test-requirements.mustache create mode 100644 modules/openapi-generator/src/main/resources/python-aiohttp/util.mustache create mode 100644 samples/server/petstore/flaskConnexion-python2/Makefile create mode 100644 samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/security_controller_.py create mode 100644 samples/server/petstore/flaskConnexion-python2/pom.xml create mode 100755 samples/server/petstore/flaskConnexion-python2/test_python2.sh create mode 100644 samples/server/petstore/flaskConnexion/Makefile create mode 100644 samples/server/petstore/flaskConnexion/openapi_server/controllers/security_controller_.py create mode 100644 samples/server/petstore/flaskConnexion/pom.xml create mode 100755 samples/server/petstore/flaskConnexion/test_python3.sh create mode 100644 samples/server/petstore/python-aiohttp/.openapi-generator-ignore create mode 100644 samples/server/petstore/python-aiohttp/.openapi-generator/VERSION create mode 100644 samples/server/petstore/python-aiohttp/Makefile create mode 100644 samples/server/petstore/python-aiohttp/README.md create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/__main__.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/controllers/__init__.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/controllers/pet_controller.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/controllers/security_controller_.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/controllers/store_controller.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/controllers/user_controller.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/models/__init__.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/models/api_response.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/models/base_model_.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/models/category.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/models/order.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/models/pet.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/models/tag.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/models/user.py create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/openapi/openapi.yaml create mode 100644 samples/server/petstore/python-aiohttp/openapi_server/util.py create mode 100644 samples/server/petstore/python-aiohttp/pom.xml create mode 100644 samples/server/petstore/python-aiohttp/requirements.txt create mode 100644 samples/server/petstore/python-aiohttp/test-requirements.txt create mode 100755 samples/server/petstore/python-aiohttp/test_python3.sh create mode 100644 samples/server/petstore/python-aiohttp/tests/__init__.py create mode 100644 samples/server/petstore/python-aiohttp/tests/conftest.py create mode 100644 samples/server/petstore/python-aiohttp/tests/test_pet_controller.py create mode 100644 samples/server/petstore/python-aiohttp/tests/test_store_controller.py create mode 100644 samples/server/petstore/python-aiohttp/tests/test_user_controller.py diff --git a/bin/python-aiohttp-petstore.sh b/bin/python-aiohttp-petstore.sh new file mode 100755 index 000000000000..1aec3f2f5172 --- /dev/null +++ b/bin/python-aiohttp-petstore.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +SCRIPT="$0" +echo "# START SCRIPT: $SCRIPT" + +while [ -h "$SCRIPT" ] ; do + ls=`ls -ld "$SCRIPT"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + SCRIPT="$link" + else + SCRIPT=`dirname "$SCRIPT"`/"$link" + fi +done + +if [ ! -d "${APP_DIR}" ]; then + APP_DIR=`dirname "$SCRIPT"`/.. + APP_DIR=`cd "${APP_DIR}"; pwd` +fi + +executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar" + +if [ ! -f "$executable" ] +then + mvn -B clean package +fi + +# if you've executed sbt assembly previously it will use that instead. +export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" +ags="generate -t modules/openapi-generator/src/main/resources/python-aiohttp -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-aiohttp -o samples/server/petstore/python-aiohttp -Dservice $@" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/python-flask-petstore-python2.sh b/bin/python-flask-petstore-python2.sh index 1b427d48f6a7..7b8ec1a68e71 100755 --- a/bin/python-flask-petstore-python2.sh +++ b/bin/python-flask-petstore-python2.sh @@ -30,5 +30,4 @@ export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/ #ags="generate -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion-python2 -DsupportPython2=true $@" ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion-python2 -c bin/supportPython2.json -D service $@" -rm -rf samples/server/petstore/flaskConnexion-python2/* java $JAVA_OPTS -jar $executable $ags diff --git a/bin/python-flask-petstore.sh b/bin/python-flask-petstore.sh index 1f599fe78797..0126e5e4624d 100755 --- a/bin/python-flask-petstore.sh +++ b/bin/python-flask-petstore.sh @@ -29,5 +29,4 @@ fi export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion -Dservice $@" -rm -rf samples/server/petstore/flaskConnexion/* java $JAVA_OPTS -jar $executable $ags diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenSecurity.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenSecurity.java index 3f267a53045b..24d665aacbb4 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenSecurity.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenSecurity.java @@ -27,7 +27,10 @@ public class CodegenSecurity { public String name; public String type; + public String scheme; public Boolean hasMore, isBasic, isOAuth, isApiKey; + // is Basic is true for all http authentication type. Those are to differentiate basic and bearer authentication + public Boolean isBasicBasic, isBasicBearer; public Map vendorExtensions = new HashMap(); // ApiKey specific public String keyParamName; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 2874cbfd29b4..686cd6046a1a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3228,6 +3228,8 @@ public List fromSecurity(Map securitySc cs.name = key; cs.type = securityScheme.getType().toString(); cs.isCode = cs.isPassword = cs.isApplication = cs.isImplicit = false; + cs.isBasicBasic = cs.isBasicBearer = false; + cs.scheme = securityScheme.getScheme(); if (SecurityScheme.Type.APIKEY.equals(securityScheme.getType())) { cs.isBasic = cs.isOAuth = false; @@ -3239,6 +3241,12 @@ public List fromSecurity(Map securitySc } else if (SecurityScheme.Type.HTTP.equals(securityScheme.getType())) { cs.isKeyInHeader = cs.isKeyInQuery = cs.isKeyInCookie = cs.isApiKey = cs.isOAuth = false; cs.isBasic = true; + if ("basic".equals(securityScheme.getScheme())) { + cs.isBasicBasic = true; + } + else if ("bearer".equals(securityScheme.getScheme())) { + cs.isBasicBearer = true; + } } else if (SecurityScheme.Type.OAUTH2.equals(securityScheme.getType())) { cs.isKeyInHeader = cs.isKeyInQuery = cs.isKeyInCookie = cs.isApiKey = cs.isBasic = false; cs.isOAuth = true; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAbstractConnexionServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAbstractConnexionServerCodegen.java index adf151f6aa51..ecfe5964f123 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAbstractConnexionServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAbstractConnexionServerCodegen.java @@ -47,6 +47,7 @@ public class PythonAbstractConnexionServerCodegen extends DefaultCodegen impleme public static final String CONTROLLER_PACKAGE = "controllerPackage"; public static final String DEFAULT_CONTROLLER = "defaultController"; public static final String SUPPORT_PYTHON2 = "supportPython2"; + static final String MEDIA_TYPE = "mediaType"; protected int serverPort = 8080; protected String packageName; @@ -54,9 +55,11 @@ public class PythonAbstractConnexionServerCodegen extends DefaultCodegen impleme protected String controllerPackage; protected String defaultController; protected Map regexModifiers; + protected boolean fixBodyName; - public PythonAbstractConnexionServerCodegen(String templateDirectory) { + public PythonAbstractConnexionServerCodegen(String templateDirectory, boolean fixBodyNameValue) { super(); + fixBodyName = fixBodyNameValue; modelPackage = "models"; testPackage = "test"; @@ -71,6 +74,8 @@ public PythonAbstractConnexionServerCodegen(String templateDirectory) { languageSpecificPrimitives.add("date"); languageSpecificPrimitives.add("file"); languageSpecificPrimitives.add("object"); + languageSpecificPrimitives.add("byte"); + languageSpecificPrimitives.add("bytearray"); typeMapping.clear(); typeMapping.put("integer", "int"); @@ -87,6 +92,8 @@ public PythonAbstractConnexionServerCodegen(String templateDirectory) { typeMapping.put("object", "object"); typeMapping.put("file", "file"); typeMapping.put("UUID", "str"); + typeMapping.put("byte", "bytearray"); + typeMapping.put("ByteArray", "bytearray"); // from https://docs.python.org/3/reference/lexical_analysis.html#keywords setReservedWordsLowerCase( @@ -124,15 +131,8 @@ public PythonAbstractConnexionServerCodegen(String templateDirectory) { * it will be processed by the template engine. Otherwise, it will be copied */ supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); - supportingFiles.add(new SupportingFile("setup.mustache", "", "setup.py")); - 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("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("Dockerfile.mustache", "", "Dockerfile")); - supportingFiles.add(new SupportingFile("dockerignore.mustache", "", ".dockerignore")); regexModifiers = new HashMap(); regexModifiers.put('i', "IGNORECASE"); @@ -156,6 +156,9 @@ public PythonAbstractConnexionServerCodegen(String templateDirectory) { defaultValue("8080")); } + protected void addSupportingFiles() { + } + @Override public void processOpts() { super.processOpts(); @@ -195,19 +198,17 @@ public void processOpts() { additionalProperties.put(SUPPORT_PYTHON2, Boolean.TRUE); typeMapping.put("long", "long"); } - supportingFiles.add(new SupportingFile("__init__.mustache", packageName, "__init__.py")); supportingFiles.add(new SupportingFile("__main__.mustache", packageName, "__main__.py")); - supportingFiles.add(new SupportingFile("encoder.mustache", packageName, "encoder.py")); supportingFiles.add(new SupportingFile("util.mustache", packageName, "util.py")); supportingFiles.add(new SupportingFile("__init__.mustache", packageName + File.separatorChar + controllerPackage, "__init__.py")); + supportingFiles.add(new SupportingFile("security_controller_.mustache", packageName + File.separatorChar + controllerPackage, "security_controller_.py")); supportingFiles.add(new SupportingFile("__init__model.mustache", packageName + File.separatorChar + modelPackage, "__init__.py")); supportingFiles.add(new SupportingFile("base_model_.mustache", packageName + File.separatorChar + modelPackage, "base_model_.py")); - supportingFiles.add(new SupportingFile("__init__test.mustache", packageName + File.separatorChar + testPackage, "__init__.py")); supportingFiles.add(new SupportingFile("openapi.mustache", packageName + File.separatorChar + "openapi", "openapi.yaml")); + addSupportingFiles(); modelPackage = packageName + "." + modelPackage; controllerPackage = packageName + "." + controllerPackage; - testPackage = packageName + "." + testPackage; } private static String dropDots(String str) { @@ -316,8 +317,27 @@ public void preprocessOpenAPI(OpenAPI openAPI) { // need vendor extensions for x-openapi-router-controller Map paths = openAPI.getPaths(); if (paths != null) { - for (String pathname : paths.keySet()) { + List pathnames = new ArrayList(paths.keySet()); + for (String pathname : pathnames) { PathItem path = paths.get(pathname); + // Fix path parameters to be in snake_case + if (pathname.contains("{")) { + String fixedPath = new String(); + for (String token: pathname.substring(1).split("/")) { + if (token.startsWith("{")) { + String snake_case_token = "{" + this.toParamName(token.substring(1, token.length()-1)) + "}"; + if(token != snake_case_token) { + token = snake_case_token; + } + } + fixedPath += "/" + token; + } + if (!fixedPath.equals(pathname)) { + LOGGER.warn("Path '" + pathname + "' is not consistant with Python variable names. It will be replaced by '" + fixedPath + "'"); + paths.remove(pathname); + paths.put(fixedPath, path); + } + } Map operationMap = path.readOperationsMap(); if (operationMap != null) { for (HttpMethod method : operationMap.keySet()) { @@ -337,9 +357,83 @@ public void preprocessOpenAPI(OpenAPI openAPI) { controllerPackage + "." + toApiFilename(tag) ); } + if (operation.getParameters() != null) { + for (Parameter parameter: operation.getParameters()) { + String swaggerParameterName = parameter.getName(); + String pythonParameterName = this.toParamName(swaggerParameterName); + if (!swaggerParameterName.equals(pythonParameterName)) { + LOGGER.warn("Parameter name '" + swaggerParameterName + "' is not consistant with Python variable names. It will be replaced by '" + pythonParameterName + "'"); + parameter.setName(pythonParameterName); + } + if (swaggerParameterName.isEmpty()) { + LOGGER.error("Missing parameter name in " + pathname + "." + parameter.getIn()); + } + } + } + RequestBody body = operation.getRequestBody(); + if (fixBodyName && body != null) { + if (body.getExtensions() == null || !body.getExtensions().containsKey("x-body-name")) { + String bodyParameterName = "body"; + if (operation.getExtensions() != null && operation.getExtensions().containsKey("x-codegen-request-body-name")) { + bodyParameterName = (String) operation.getExtensions().get("x-codegen-request-body-name"); + } + else { + // Used by code generator + operation.addExtension("x-codegen-request-body-name", bodyParameterName); + } + // Used by connexion + body.addExtension("x-body-name", bodyParameterName); + } + } } } } + // Sort path names after variable name fix + List fixedPathnames = new ArrayList(paths.keySet()); + Collections.sort(fixedPathnames); + for (String pathname: fixedPathnames) { + PathItem pathItem = paths.remove(pathname); + paths.put(pathname, pathItem); + } + } + addSecurityExtensions(openAPI); + } + + private void addSecurityExtension(SecurityScheme securityScheme, String extensionName, String functionName) { + if (securityScheme.getExtensions() == null || ! securityScheme.getExtensions().containsKey(extensionName)) { + securityScheme.addExtension(extensionName, functionName); + } + } + + private void addSecurityExtensions(OpenAPI openAPI) { + Components components = openAPI.getComponents(); + if (components != null && components.getSecuritySchemes() != null) { + Map securitySchemes = components.getSecuritySchemes(); + for(String securityName: securitySchemes.keySet()) { + SecurityScheme securityScheme = securitySchemes.get(securityName); + String baseFunctionName = controllerPackage + ".security_controller_."; + switch(securityScheme.getType()) { + case APIKEY: + addSecurityExtension(securityScheme, "x-apikeyInfoFunc", baseFunctionName + "info_from_" + securityName); + break; + case HTTP: + if ("basic".equals(securityScheme.getScheme())) { + addSecurityExtension(securityScheme, "x-basicInfoFunc", baseFunctionName + "info_from_" + securityName); + } + else if ("bearer".equals(securityScheme.getScheme())) { + addSecurityExtension(securityScheme, "x-bearerInfoFunc", baseFunctionName + "info_from_" + securityName); + } + break; + case OPENIDCONNECT: + LOGGER.warn("Security type " + securityScheme.getType().toString() + " is not supported by connextion yet"); + case OAUTH2: + addSecurityExtension(securityScheme, "x-tokenInfoFunc", baseFunctionName + "info_from_" + securityName); + addSecurityExtension(securityScheme, "x-scopeValidateFunc", baseFunctionName + "validate_scope_" + securityName); + break; + default: + LOGGER.warn("Unknown security type " + securityScheme.getType().toString()); + } + } } } @@ -586,7 +680,7 @@ public void setParameterExampleValue(CodegenParameter p) { example = "'" + escapeText(example) + "'"; } else if (!languageSpecificPrimitives.contains(type)) { // type is a model class, e.g. User - example = type + "()"; + example = "{}"; } else { LOGGER.warn("Type " + type + " not handled properly in setParameterExampleValue"); } @@ -595,7 +689,12 @@ public void setParameterExampleValue(CodegenParameter p) { example = p.items.defaultValue; } if (example == null) { - example = "None"; + if (Boolean.TRUE.equals(p.isListContainer)) { + example = "[]"; + } + else { + example = "None"; + } } else if (Boolean.TRUE.equals(p.isListContainer)) { if (Boolean.TRUE.equals(p.isBodyParam)) { example = "[" + example + "]"; @@ -657,11 +756,98 @@ public Map postProcessModels(Map objs) { return postProcessModelsEnum(objs); } + @Override + public Map postProcessAllModels(Map objs) { + Map result = super.postProcessAllModels(objs); + for (Map.Entry entry : result.entrySet()) { + Map inner = (Map) entry.getValue(); + List> models = (List>) inner.get("models"); + for (Map mo : models) { + CodegenModel cm = (CodegenModel) mo.get("model"); + // Add additional filename information for imports + mo.put("pyImports", toPyImports(cm, cm.imports)); + } + } + return result; + } + + private List> toPyImports(CodegenModel cm, Set imports) { + List> pyImports = new ArrayList<>(); + for (String im : imports) { + if (!im.equals(cm.classname)) { + HashMap pyImport = new HashMap<>(); + pyImport.put("import", toModelImport(im)); + pyImports.add(pyImport); + } + } + return pyImports; + } + @Override public void postProcessParameter(CodegenParameter parameter) { postProcessPattern(parameter.pattern, parameter.vendorExtensions); } + @Override + public Map postProcessOperationsWithModels(Map objs, List allModels) { + Map operations = (Map) objs.get("operations"); + List operationList = (List) operations.get("operation"); + + for (CodegenOperation operation : operationList) { + Map skipTests = new HashMap<>(); + // Set flag to deactivate tests due to connexion issue. + if (operation.consumes != null ) { + if (operation.consumes.size() == 1) { + Map consume = operation.consumes.get(0); + if (! "application/json".equals(consume.get(MEDIA_TYPE))) { + skipTests.put("reason", consume.get(MEDIA_TYPE) + " not supported by Connexion"); + if ("multipart/form-data".equals(consume.get(MEDIA_TYPE))) { + operation.isMultipart = Boolean.TRUE; + } + } + operation.vendorExtensions.put("x-prefered-consume", consume); + } + else if (operation.consumes.size() > 1) { + Map consume = operation.consumes.get(0); + skipTests.put("reason", "Connexion does not support multiple consummes. See https://github.com/zalando/connexion/pull/760"); + operation.vendorExtensions.put("x-prefered-consume", consume); + if ("multipart/form-data".equals(consume.get(MEDIA_TYPE))) { + operation.isMultipart = Boolean.TRUE; + } + } + } + else { + // A body without consumes means '*/*' has been used instead of application/json + if (operation.bodyParam != null) { + Map consume = new HashMap<>(); + consume.put(MEDIA_TYPE, "application/json"); + operation.vendorExtensions.put("x-prefered-consume", consume); + skipTests.put("reason", "*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760"); + } + } + // Choose to consume 'application/json' if available, else choose the last one. + if (operation.produces != null ) { + for (Map produce: operation.produces) { + operation.vendorExtensions.put("x-prefered-produce", produce); + if (produce.get(MEDIA_TYPE).equals("application/json")) { + break; + } + } + } + if (! skipTests.isEmpty()) { + operation.vendorExtensions.put("x-skip-test", skipTests); + } + if (operation.requestBodyExamples != null) { + for (Map example: operation.requestBodyExamples) { + if (example.get("contentType") != null && example.get("contentType").equals("application/json")) { + operation.bodyParam.example = example.get("example"); + } + } + } + } + return objs; + } + /* * The openapi 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 diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAiohttpConnexionServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAiohttpConnexionServerCodegen.java new file mode 100644 index 000000000000..bcdd450c7031 --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonAiohttpConnexionServerCodegen.java @@ -0,0 +1,49 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openapitools.codegen.languages; + +import org.openapitools.codegen.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.File; +import java.util.*; + +public class PythonAiohttpConnexionServerCodegen extends PythonAbstractConnexionServerCodegen { + private static final Logger LOGGER = LoggerFactory.getLogger(PythonAiohttpConnexionServerCodegen.class); + + public PythonAiohttpConnexionServerCodegen() { + super("python-aiohttp", true); + testPackage = "tests"; + embeddedTemplateDir = templateDir = "python-aiohttp"; + } + + /** + * Configures a friendly name for the generator. This will be used by the generator + * to select the library with the -g flag. + * + * @return the friendly name for the generator + */ + @Override + public String getName() { + return "python-aiohttp"; + } + + @Override + protected void addSupportingFiles() { + supportingFiles.add(new SupportingFile("conftest.mustache", testPackage, "conftest.py")); + supportingFiles.add(new SupportingFile("__init__test.mustache", testPackage, "__init__.py")); + } +} diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFlaskConnexionServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFlaskConnexionServerCodegen.java index d83b1bec2dac..468d32e0210a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFlaskConnexionServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFlaskConnexionServerCodegen.java @@ -16,8 +16,10 @@ package org.openapitools.codegen.languages; +import org.openapitools.codegen.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; import java.util.*; import static org.openapitools.codegen.utils.StringUtils.camelize; @@ -27,7 +29,7 @@ public class PythonFlaskConnexionServerCodegen extends PythonAbstractConnexionSe private static final Logger LOGGER = LoggerFactory.getLogger(PythonFlaskConnexionServerCodegen.class); public PythonFlaskConnexionServerCodegen() { - super("flaskConnexion"); + super("flaskConnexion", false); } /** @@ -40,4 +42,19 @@ public PythonFlaskConnexionServerCodegen() { public String getName() { return "python-flask"; } + + @Override + protected void addSupportingFiles() { + supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); + supportingFiles.add(new SupportingFile("Dockerfile.mustache", "", "Dockerfile")); + supportingFiles.add(new SupportingFile("dockerignore.mustache", "", ".dockerignore")); + supportingFiles.add(new SupportingFile("setup.mustache", "", "setup.py")); + supportingFiles.add(new SupportingFile("tox.mustache", "", "tox.ini")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); + supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml")); + supportingFiles.add(new SupportingFile("encoder.mustache", packageName, "encoder.py")); + supportingFiles.add(new SupportingFile("__init__test.mustache", packageName + File.separatorChar + testPackage, "__init__.py")); + supportingFiles.add(new SupportingFile("__init__.mustache", packageName, "__init__.py")); + testPackage = packageName + "." + testPackage; + } } 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 910a76f04e2c..28447fe92b2c 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 @@ -74,6 +74,7 @@ org.openapitools.codegen.languages.PhpZendExpressivePathHandlerServerCodegen org.openapitools.codegen.languages.PowerShellClientCodegen org.openapitools.codegen.languages.PythonClientCodegen org.openapitools.codegen.languages.PythonFlaskConnexionServerCodegen +org.openapitools.codegen.languages.PythonAiohttpConnexionServerCodegen org.openapitools.codegen.languages.RClientCodegen org.openapitools.codegen.languages.RubyClientCodegen org.openapitools.codegen.languages.RubyOnRailsServerCodegen diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/controller_test.mustache b/modules/openapi-generator/src/main/resources/flaskConnexion/controller_test.mustache index a41b12f2c210..44f6d1ea19f4 100644 --- a/modules/openapi-generator/src/main/resources/flaskConnexion/controller_test.mustache +++ b/modules/openapi-generator/src/main/resources/flaskConnexion/controller_test.mustache @@ -1,6 +1,7 @@ # coding: utf-8 from __future__ import absolute_import +import unittest from flask import json from six import BytesIO @@ -13,7 +14,10 @@ from {{packageName}}.test import BaseTestCase class {{#operations}}Test{{classname}}(BaseTestCase): """{{classname}} integration test stubs""" - {{#operation}} + {{#operation}} + {{#vendorExtensions.x-skip-test}} + @unittest.skip("{{reason}}") + {{/vendorExtensions.x-skip-test}} def test_{{operationId}}(self): """Test case for {{{operationId}}} @@ -25,18 +29,21 @@ class {{#operations}}Test{{classname}}(BaseTestCase): {{#queryParams}} {{#-first}}query_string = [{{/-first}}{{^-first}} {{/-first}}('{{paramName}}', {{{example}}}){{#hasMore}},{{/hasMore}}{{#-last}}]{{/-last}} {{/queryParams}} - {{#headerParams}} - {{#-first}}headers = [{{/-first}}{{^-first}} {{/-first}}('{{paramName}}', {{{example}}}){{#hasMore}},{{/hasMore}}{{#-last}}]{{/-last}} - {{/headerParams}} + headers = { {{#vendorExtensions.x-prefered-produce}} + 'Accept': '{{mediaType}}',{{/vendorExtensions.x-prefered-produce}}{{#vendorExtensions.x-prefered-consume}} + 'Content-Type': '{{mediaType}}',{{/vendorExtensions.x-prefered-consume}}{{#headerParams}} + '{{paramName}}': {{{example}}},{{/headerParams}}{{#authMethods}} + {{#isOAuth}}'Authorization': 'Bearer special-key',{{/isOAuth}}{{#isApiKey}}'{{name}}': 'special-key',{{/isApiKey}}{{#isBasic}}'Authorization': '{{#isBasicBasic}}{{#isBasicBasic}} BasicZm9vOmJhcg=={{#isBasicBearer}}Bearer special-key{{#isBasicBearer}}',{{/isBasic}}{{/authMethods}} + } {{#formParams}} {{#-first}}data = dict({{/-first}}{{^-first}} {{/-first}}{{paramName}}={{{example}}}{{#hasMore}},{{/hasMore}}{{#-last}}){{/-last}} {{/formParams}} response = self.client.open( '{{#contextPath}}{{{.}}}{{/contextPath}}{{{path}}}'{{#pathParams}}{{#-first}}.format({{/-first}}{{paramName}}={{{example}}}{{#hasMore}}, {{/hasMore}}{{^hasMore}}){{/hasMore}}{{/pathParams}}, - method='{{httpMethod}}'{{#bodyParam}}, + method='{{httpMethod}}', + headers=headers{{#bodyParam}}, data=json.dumps({{paramName}}){{^consumes}}, - content_type='application/json'{{/consumes}}{{/bodyParam}}{{#headerParams}}{{#-first}}, - headers=headers{{/-first}}{{/headerParams}}{{#formParams}}{{#-first}}, + content_type='application/json'{{/consumes}}{{/bodyParam}}{{#formParams}}{{#-first}}, data=data{{/-first}}{{/formParams}}{{#consumes}}{{#-first}}, content_type='{{{mediaType}}}'{{/-first}}{{/consumes}}{{#queryParams}}{{#-first}}, query_string=query_string{{/-first}}{{/queryParams}}) @@ -47,5 +54,4 @@ class {{#operations}}Test{{classname}}(BaseTestCase): {{/operations}} if __name__ == '__main__': - import unittest unittest.main() diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/model.mustache b/modules/openapi-generator/src/main/resources/flaskConnexion/model.mustache index 66db2b5bd951..6b6743d5e663 100644 --- a/modules/openapi-generator/src/main/resources/flaskConnexion/model.mustache +++ b/modules/openapi-generator/src/main/resources/flaskConnexion/model.mustache @@ -6,6 +6,13 @@ from datetime import date, datetime # noqa: F401 from typing import List, Dict # noqa: F401 from {{modelPackage}}.base_model_ import Model +{{#models}} +{{#model}} +{{#pyImports}} +{{import}} +{{/pyImports}} +{{/model}} +{{/models}} from {{packageName}} import util {{#imports}} diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/requirements.mustache b/modules/openapi-generator/src/main/resources/flaskConnexion/requirements.mustache index 2a194f98782a..8d4d653bfd7f 100644 --- a/modules/openapi-generator/src/main/resources/flaskConnexion/requirements.mustache +++ b/modules/openapi-generator/src/main/resources/flaskConnexion/requirements.mustache @@ -1,4 +1,4 @@ -connexion == 2.0.0 +connexion == 2.0.2 swagger-ui-bundle == 0.0.2 python_dateutil == 2.6.0 {{#supportPython2}} diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/security_controller_.mustache b/modules/openapi-generator/src/main/resources/flaskConnexion/security_controller_.mustache new file mode 100644 index 000000000000..10f248927979 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/flaskConnexion/security_controller_.mustache @@ -0,0 +1,43 @@ +{{#authMethods}} +{{#isOAuth}} + +def info_from_token_{{name}}(token): + """ + Validate and decode token. + Returned value will be available in request['token_info']. + 'sub' or 'uid' will be set in request['user']. + 'scope' or 'scopes' will be passed to scope validation function. + Should return None if token is invalid + """ + return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + + +def validate_scope_{{name}}(required_scopes, token_scopes): + """ Validate required scopes are included in token scope """ + return set(required_scopes).issubset(set(token_scopes)) + +{{/isOAuth}} +{{#isApiKey}} + +def info_from_api_key_{{name}}(api_key, required_scopes): + """ + Returned value will be available in request['token_info']. + 'sub' or 'uid' will be set in request['user']. + Should return None if api_key is invalid + """ + return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + +{{/isApiKey}} +{{#isBasic}} + +def info_from_basic_auth_{{name}}(username, password, required_scopes): + """ + Returned value will be available in request['token_info']. + 'sub' or 'uid' will be set in request['user']. + Should return None if auth is invalid + """ + return {'scopes': ['read:pets', 'write:pets'], 'uid': username} + +{{/isBasic}} +{{/authMethods}} + diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/tox.mustache b/modules/openapi-generator/src/main/resources/flaskConnexion/tox.mustache index 3efa994317d9..1195b3391b0b 100644 --- a/modules/openapi-generator/src/main/resources/flaskConnexion/tox.mustache +++ b/modules/openapi-generator/src/main/resources/flaskConnexion/tox.mustache @@ -1,5 +1,5 @@ [tox] -envlist = {{#supportPython2}}py27, {{/supportPython2}}py35 +envlist = {{#supportPython2}}py27, {{/supportPython2}}py3 [testenv] deps=-r{toxinidir}/requirements.txt diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/README.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/README.mustache new file mode 100644 index 000000000000..3402bd835d9d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/README.mustache @@ -0,0 +1,46 @@ +# OpenAPI generated server + +## Overview +This server was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the +[OpenAPI-Spec](https://openapis.org) from a remote server, you can easily generate a server stub. This +is an example of building a OpenAPI-enabled aiohtpp server. + +This example uses the [Connexion](https://github.com/zalando/connexion) library on top of aiohtpp. + +## Requirements +Python 3.5.2+ + +## Usage +To run the server, please execute the following from the root directory: + +``` +pip3 install -r requirements.txt +python3 -m {{packageName}} +``` + +and open your browser to here: + +``` +http://localhost:{{serverPort}}{{contextPath}}/ui/ +``` + +Your OpenAPI definition lives here: + +``` +http://localhost:{{serverPort}}{{contextPath}}/openapi.json +``` + +To launch the integration tests, use pytest: +``` +sudo pip install -r test-requirements.txt +pytest +``` + +## Prevent file overriding + +After first generation, add edited files to _.openapi-generator-ignore_ to prevent generator to overwrite them. Typically: +``` +server/controllers/* +test/* +*.txt +``` diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/__init__.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/__init__.mustache new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/__init__main.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/__init__main.mustache new file mode 100644 index 000000000000..c164cd99e621 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/__init__main.mustache @@ -0,0 +1,12 @@ +import os +import connexion + + +def main(): + options = { + "swagger_ui": True + } + specification_dir = os.path.join(os.path.dirname(__file__), 'openapi') + app = connexion.AioHttpApp(__name__, specification_dir=specification_dir, options=options) + app.add_api('openapi.yaml', arguments={'title': '{{appName}}'}, pass_context_arg_name='request') + app.run(port={{serverPort}}) diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/__init__model.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/__init__model.mustache new file mode 100644 index 000000000000..654db8044f1f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/__init__model.mustache @@ -0,0 +1,5 @@ +# coding: utf-8 + +# import models into model package +{{#models}}{{#model}}from {{modelPackage}}.{{classFilename}} import {{classname}}{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/__init__test.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/__init__test.mustache new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/__main__.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/__main__.mustache new file mode 100644 index 000000000000..f20ae81db34e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/__main__.mustache @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 + +from . import main + +if __name__ == '__main__': + main() diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/base_model_.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/base_model_.mustache new file mode 100644 index 000000000000..163d1b096e50 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/base_model_.mustache @@ -0,0 +1,66 @@ +import pprint + +import typing + +from {{packageName}} import util + +T = typing.TypeVar('T') + + +class Model(object): + # openapiTypes: The key is attribute name and the + # value is attribute type. + openapi_types = {} + + # attributeMap: The key is attribute name and the + # value is json key in definition. + attribute_map = {} + + @classmethod + def from_dict(cls: T, dikt: dict) -> T: + """Returns the dict as a model""" + return util.deserialize_model(dikt, cls) + + def to_dict(self) -> dict: + """Returns the model properties as a dict + """ + result = {} + + for attr_key, json_key in self.attribute_map.items(): + value = getattr(self, attr_key) + if value is None: + continue + if isinstance(value, list): + result[json_key] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[json_key] = value.to_dict() + elif isinstance(value, dict): + result[json_key] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[json_key] = value + + return result + + def to_str(self) -> str: + """Returns the string representation of the model + """ + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/conftest.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/conftest.mustache new file mode 100644 index 000000000000..a90e6b0e22bc --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/conftest.mustache @@ -0,0 +1,17 @@ +import logging +import pytest +import os + +import connexion + + +@pytest.fixture +def client(loop, aiohttp_client): + logging.getLogger('connexion.operation').setLevel('ERROR') + options = { + "swagger_ui": True + } + specification_dir = os.path.join(os.path.dirname(__file__), '..', '{{packageName}}', 'openapi') + app = connexion.AioHttpApp(__name__, specification_dir=specification_dir, options=options) + app.add_api('openapi.yaml', pass_context_arg_name='request') + return loop.run_until_complete(aiohttp_client(app.app)) diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/controller.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/controller.mustache new file mode 100644 index 000000000000..c9adc6c12ceb --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/controller.mustache @@ -0,0 +1,104 @@ +from typing import List, Dict +from aiohttp import web + +{{#imports}}{{import}} +{{/imports}} +from {{packageName}} import util +{{#operations}} +{{#operation}} + + +async def {{operationId}}(request: web.Request, {{#allParams}}{{paramName}}{{^required}}=None{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> web.Response: + """{{#summary}}{{.}}{{/summary}}{{^summary}}{{operationId}}{{/summary}} + + {{#notes}}{{.}}{{/notes}} + + {{#allParams}} + :param {{paramName}}: {{description}} + {{^isContainer}} + {{#isPrimitiveType}} + :type {{paramName}}: {{>param_type}} + {{/isPrimitiveType}} + {{#isUuid}} + :type {{paramName}}: {{>param_type}} + {{/isUuid}} + {{^isPrimitiveType}} + {{#isFile}} + :type {{paramName}}: werkzeug.datastructures.FileStorage + {{/isFile}} + {{^isFile}} + {{^isUuid}} + :type {{paramName}}: dict | bytes + {{/isUuid}} + {{/isFile}} + {{/isPrimitiveType}} + {{/isContainer}} + {{#isListContainer}} + {{#items}} + {{#isPrimitiveType}} + :type {{paramName}}: List[{{>param_type}}] + {{/isPrimitiveType}} + {{^isPrimitiveType}} + :type {{paramName}}: list | bytes + {{/isPrimitiveType}} + {{/items}} + {{/isListContainer}} + {{#isMapContainer}} + {{#items}} + {{#isPrimitiveType}} + :type {{paramName}}: Dict[str, {{>param_type}}] + {{/isPrimitiveType}} + {{^isPrimitiveType}} + :type {{paramName}}: dict | bytes + {{/isPrimitiveType}} + {{/items}} + {{/isMapContainer}} + {{/allParams}} + + """ + {{#allParams}} + {{^isContainer}} + {{#isDate}} + {{paramName}} = util.deserialize_date({{paramName}}) + {{/isDate}} + {{#isDateTime}} + {{paramName}} = util.deserialize_datetime({{paramName}}) + {{/isDateTime}} + {{^isPrimitiveType}} + {{^isFile}} + {{^isUuid}} + {{paramName}} = {{baseType}}.from_dict({{paramName}}) + {{/isUuid}} + {{/isFile}} + {{/isPrimitiveType}} + {{/isContainer}} + {{#isListContainer}} + {{#items}} + {{#isDate}} + {{paramName}} = [util.deserialize_date(s) for s in {{paramName}}] + {{/isDate}} + {{#isDateTime}} + {{paramName}} = [util.deserialize_datetime(s) for s in {{paramName}}] + {{/isDateTime}} + {{#complexType}} + {{paramName}} = [{{complexType}}.from_dict(d) for d in {{paramName}}] + {{/complexType}} + {{/items}} + {{/isListContainer}} + {{#isMapContainer}} + {{#items}} + {{#isDate}} + {{paramName}} = {k: util.deserialize_date(v) for k, v in {{paramName}}} + {{/isDate}} + {{#isDateTime}} + {{paramName}} = {k: util.deserialize_datetime(v) for k, v in {{paramName}}} + {{/isDateTime}} + {{#complexType}} + {{paramName}} = {k: {{baseType}}.from_dict(v) for k, v in {{paramName}}} + {{/complexType}} + {{/items}} + {{/isMapContainer}} + {{/allParams}} + return web.Response(status=200) +{{/operation}} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/controller_test.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/controller_test.mustache new file mode 100644 index 000000000000..a53f02882716 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/controller_test.mustache @@ -0,0 +1,68 @@ +# coding: utf-8 + +import pytest +import json +from aiohttp import web +{{#operations}} +{{#operation}} +{{#isMultipart}} +from aiohttp import FormData +{{/isMultipart}} +{{/operation}} +{{/operations}} + +{{#imports}}{{import}} +{{/imports}} + +{{#operations}} +{{#operation}} + +{{#vendorExtensions.x-skip-test}} +@pytest.mark.skip("{{reason}}") +{{/vendorExtensions.x-skip-test}} +async def test_{{operationId}}(client): + """Test case for {{{operationId}}} + + {{{summary}}} + """ + {{#bodyParam}} + {{paramName}} = {{{example}}} + {{/bodyParam}} + {{#queryParams}} + {{#-first}}params = [{{/-first}}{{^-first}} {{/-first}}('{{paramName}}', {{{example}}}){{#hasMore}},{{/hasMore}}{{#-last}}]{{/-last}} + {{/queryParams}} + headers = { {{#vendorExtensions.x-prefered-produce}} + 'Accept': '{{mediaType}}',{{/vendorExtensions.x-prefered-produce}}{{#vendorExtensions.x-prefered-consume}} + 'Content-Type': '{{mediaType}}',{{/vendorExtensions.x-prefered-consume}}{{#headerParams}} + '{{paramName}}': {{{example}}},{{/headerParams}}{{#authMethods}} + {{#isOAuth}}'Authorization': 'Bearer special-key',{{/isOAuth}}{{#isApiKey}}'{{name}}': 'special-key',{{/isApiKey}}{{#isBasicBasic}}'Authorization': 'BasicZm9vOmJhcg==',{{/isBasicBasic}}{{#isBasicBearer}}'Authorization': 'Bearer special-key',{{/isBasicBearer}}{{/authMethods}} + } + {{#formParams}} + {{#isMultipart}} + {{#-first}} + data = FormData() + {{/-first}} + data.add_field('{{paramName}}', {{{example}}}) + {{/isMultipart}} + {{^isMultipart}} + {{#-first}} + data = { + {{/-first}} + '{{paramName}}': {{{example}}}{{#hasMore}},{{/hasMore}} + {{^hasMore}} + } + {{/hasMore}} + {{/isMultipart}} + {{/formParams}} + response = await client.request( + method='{{httpMethod}}', + path='{{#contextPath}}{{{.}}}{{/contextPath}}{{{path}}}'{{#pathParams}}{{#-first}}.format({{/-first}}{{paramName}}={{{example}}}{{#hasMore}}, {{/hasMore}}{{^hasMore}}){{/hasMore}}{{/pathParams}}, + headers=headers,{{#bodyParam}} + json={{paramName}},{{/bodyParam}}{{#formParams}}{{#-first}} + data=data,{{/-first}}{{/formParams}}{{#queryParams}}{{#-first}} + params=params,{{/-first}}{{/queryParams}} + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + +{{/operation}} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/model.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/model.mustache new file mode 100644 index 000000000000..9ab013ea9ef5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/model.mustache @@ -0,0 +1,162 @@ +# coding: utf-8 + +from datetime import date, datetime + +from typing import List, Dict, Type + +from {{modelPackage}}.base_model_ import Model +{{#models}} +{{#model}} +{{#pyImports}} +{{import}} +{{/pyImports}} +{{/model}} +{{/models}} +from {{packageName}} import util + + +{{#models}} +{{#model}} +class {{classname}}(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """{{#allowableValues}} + + """ + allowed enum values + """ +{{#enumVars}} + {{name}} = {{{value}}}{{^-last}} +{{/-last}} +{{/enumVars}}{{/allowableValues}} + + def __init__(self{{#vars}}, {{name}}: {{dataType}}={{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}None{{/defaultValue}}{{/vars}}): + """{{classname}} - a model defined in OpenAPI + + {{#vars}} + :param {{name}}: The {{name}} of this {{classname}}. + {{/vars}} + """ + self.openapi_types = { +{{#vars}} + '{{name}}': {{dataType}}{{#hasMore}},{{/hasMore}} +{{/vars}} + } + + self.attribute_map = { +{{#vars}} + '{{name}}': '{{baseName}}'{{#hasMore}},{{/hasMore}} +{{/vars}} + } +{{#vars}}{{#-first}} +{{/-first}} + self._{{name}} = {{name}} +{{/vars}} + + @classmethod + def from_dict(cls, dikt: dict) -> '{{classname}}': + """Returns the dict as a model + + :param dikt: A dict. + :return: The {{name}} of this {{classname}}. + """ + return util.deserialize_model(dikt, cls){{#vars}}{{#-first}} + +{{/-first}} + @property + def {{name}}(self): + """Gets the {{name}} of this {{classname}}. + + {{#description}} + {{{description}}} + {{/description}} + + :return: The {{name}} of this {{classname}}. + :rtype: {{dataType}} + """ + return self._{{name}} + + @{{name}}.setter + def {{name}}(self, {{name}}): + """Sets the {{name}} of this {{classname}}. + + {{#description}} + {{{description}}} + {{/description}} + + :param {{name}}: The {{name}} of this {{classname}}. + :type {{name}}: {{dataType}} + """ +{{#isEnum}} + allowed_values = [{{#allowableValues}}{{#values}}"{{{this}}}"{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}}] +{{#isContainer}} +{{#isListContainer}} + if not set({{{name}}}).issubset(set(allowed_values)): + raise ValueError( + "Invalid values for `{{{name}}}` [{0}], must be a subset of [{1}]" + .format(", ".join(map(str, set({{{name}}}) - set(allowed_values))), + ", ".join(map(str, allowed_values))) + ) +{{/isListContainer}} +{{#isMapContainer}} + if not set({{{name}}}.keys()).issubset(set(allowed_values)): + raise ValueError( + "Invalid keys in `{{{name}}}` [{0}], must be a subset of [{1}]" + .format(", ".join(map(str, set({{{name}}}.keys()) - set(allowed_values))), + ", ".join(map(str, allowed_values))) + ) +{{/isMapContainer}} +{{/isContainer}} +{{^isContainer}} + if {{{name}}} not in allowed_values: + raise ValueError( + "Invalid value for `{{{name}}}` ({0}), must be one of {1}" + .format({{{name}}}, allowed_values) + ) +{{/isContainer}} +{{/isEnum}} +{{^isEnum}} +{{#required}} + if {{name}} is None: + raise ValueError("Invalid value for `{{name}}`, must not be `None`") +{{/required}} +{{#hasValidation}} +{{#maxLength}} + if {{name}} is not None and len({{name}}) > {{maxLength}}: + raise ValueError("Invalid value for `{{name}}`, length must be less than or equal to `{{maxLength}}`") +{{/maxLength}} +{{#minLength}} + if {{name}} is not None and len({{name}}) < {{minLength}}: + raise ValueError("Invalid value for `{{name}}`, length must be greater than or equal to `{{minLength}}`") +{{/minLength}} +{{#maximum}} + if {{name}} is not None and {{name}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}}: + raise ValueError("Invalid value for `{{name}}`, must be a value less than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}`{{maximum}}`") +{{/maximum}} +{{#minimum}} + if {{name}} is not None and {{name}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}}: + raise ValueError("Invalid value for `{{name}}`, must be a value greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}`{{minimum}}`") +{{/minimum}} +{{#pattern}} + if {{name}} is not None and not re.search(r'{{{vendorExtensions.x-regex}}}', {{name}}{{#vendorExtensions.x-modifiers}}{{#-first}}, flags={{/-first}}re.{{.}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}}): + raise ValueError("Invalid value for `{{name}}`, must be a follow pattern or equal to `{{{pattern}}}`") +{{/pattern}} +{{#maxItems}} + if {{name}} is not None and len({{name}}) > {{maxItems}}: + raise ValueError("Invalid value for `{{name}}`, number of items must be less than or equal to `{{maxItems}}`") +{{/maxItems}} +{{#minItems}} + if {{name}} is not None and len({{name}}) < {{minItems}}: + raise ValueError("Invalid value for `{{name}}`, number of items must be greater than or equal to `{{minItems}}`") +{{/minItems}} +{{/hasValidation}} +{{/isEnum}} + + self._{{name}} = {{name}}{{^-last}} + +{{/-last}} +{{/vars}} + +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/openapi.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/openapi.mustache new file mode 100644 index 000000000000..51ebafb0187d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/openapi.mustache @@ -0,0 +1 @@ +{{{openapi-yaml}}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/param_type.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/param_type.mustache new file mode 100644 index 000000000000..21e7e07ec530 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/param_type.mustache @@ -0,0 +1 @@ +{{#isString}}str{{/isString}}{{#isInteger}}int{{/isInteger}}{{#isLong}}int{{/isLong}}{{#isFloat}}float{{/isFloat}}{{#isDouble}}float{{/isDouble}}{{#isByteArray}}str{{/isByteArray}}{{#isBinary}}str{{/isBinary}}{{#isBoolean}}bool{{/isBoolean}}{{#isDate}}str{{/isDate}}{{#isDateTime}}str{{/isDateTime}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache new file mode 100644 index 000000000000..835c75de58a6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache @@ -0,0 +1,3 @@ +connexion[aiohttp,swagger-ui] == 2.0.2 +swagger-ui-bundle == 0.0.2 +aiohttp_jinja2 == 1.1.0 diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/security_controller_.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/security_controller_.mustache new file mode 100644 index 000000000000..04aeecc10460 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/security_controller_.mustache @@ -0,0 +1,59 @@ +from typing import List + +{{#authMethods}} +{{#isOAuth}} + +def info_from_{{name}}(token: str) -> dict: + """ + Validate and decode token. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. + 'scope' or 'scopes' will be passed to scope validation function. + Should return None if token is invalid + """ + return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + + +def validate_scope_{{name}}(required_scopes: List[str], token_scopes: List[str]) -> bool: + """ Validate required scopes are included in token scope """ + return set(required_scopes).issubset(set(token_scopes)) + +{{/isOAuth}} +{{#isApiKey}} + +def info_from_{{name}}(api_key: str, required_scopes: None) -> dict: + """ + Check and retrieve authentication information from api_key. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. + Should return None if api_key is invalid + """ + return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + +{{/isApiKey}} +{{#isBasicBasic}} + +def info_from_{{name}}(username: str, password: str, required_scopes: None) -> dict: + """ + Check and retrieve authentication information from basic auth. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. + Should return None if auth is invalid + """ + return {'scopes': ['read:pets', 'write:pets'], 'uid': username} + +{{/isBasicBasic}} +{{#isBasicBearer}} + +def info_from_{{name}}(token: str) -> dict: + """ + Check and retrieve authentication information from custom bearer token. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. + Should return None if auth is invalid + """ + return {'scopes': ['read:pets', 'write:pets'], 'uid': 'username'} + +{{/isBasicBearer}} +{{/authMethods}} + diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/test-requirements.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/test-requirements.mustache new file mode 100644 index 000000000000..1cb425f65519 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/test-requirements.mustache @@ -0,0 +1,6 @@ +coverage>=4.0.3 +pytest>=1.3.7 +pluggy>=0.3.1 +py>=1.4.31 +randomize>=0.13 +pytest-aiohttp>=0.3.0 diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/util.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/util.mustache new file mode 100644 index 000000000000..9263acb016ea --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/util.mustache @@ -0,0 +1,130 @@ +import datetime + +import typing +from typing import Union + +T = typing.TypeVar('T') +Class = typing.Type[T] + + +def _deserialize(data: Union[dict, list, str], klass: Union[Class, str]) -> Union[dict, list, Class, int, float, str, bool, datetime.date, datetime.datetime]: + """Deserializes dict, list, str into an object. + + :param data: dict, list or str. + :param klass: class literal, or string of class name. + + :return: object. + """ + if data is None: + return None + + if klass in (int, float, str, bool): + return _deserialize_primitive(data, klass) + elif klass == object: + return _deserialize_object(data) + elif klass == datetime.date: + return deserialize_date(data) + elif klass == datetime.datetime: + return deserialize_datetime(data) + elif type(klass) == typing.GenericMeta: + if klass.__extra__ == list: + return _deserialize_list(data, klass.__args__[0]) + if klass.__extra__ == dict: + return _deserialize_dict(data, klass.__args__[1]) + else: + return deserialize_model(data, klass) + + +def _deserialize_primitive(data, klass: Class) -> Union[Class, int, float, str, bool]: + """Deserializes to primitive type. + + :param data: data to deserialize. + :param klass: class literal. + + :return: int, float, str, bool. + """ + try: + value = klass(data) + except (UnicodeEncodeError, TypeError): + value = data + return value + + +def _deserialize_object(value: T) -> T: + """Return an original value. + + :return: object. + """ + return value + + +def deserialize_date(string: str) -> datetime.date: + """Deserializes string to date. + + :param string: str. + :return: date. + """ + try: + from dateutil.parser import parse + return parse(string).date() + except ImportError: + return string + + +def deserialize_datetime(string: str) -> datetime.datetime: + """Deserializes string to datetime. + + The string should be in iso8601 datetime format. + + :param string: str. + :return: datetime. + """ + try: + from dateutil.parser import parse + return parse(string) + except ImportError: + return string + + +def deserialize_model(data: Union[dict, list], klass: T) -> T: + """Deserializes list or dict to model. + + :param data: dict, list. + :param klass: class literal. + :return: model object. + """ + instance = klass() + + if not instance.openapi_types: + return data + + if data is not None and isinstance(data, (list, dict)): + for attr, attr_type in instance.openapi_types.items(): + attr_key = instance.attribute_map[attr] + if attr_key in data: + value = data[attr_key] + setattr(instance, attr, _deserialize(value, attr_type)) + + return instance + + +def _deserialize_list(data: list, boxed_type) -> list: + """Deserializes a list and its elements. + + :param data: list to deserialize. + :param boxed_type: class literal. + + :return: deserialized list. + """ + return [_deserialize(sub_data, boxed_type) for sub_data in data] + + +def _deserialize_dict(data: dict, boxed_type) -> dict: + """Deserializes a dict and its elements. + + :param data: dict to deserialize. + :param boxed_type: class literal. + + :return: deserialized dict. + """ + return {k: _deserialize(v, boxed_type) for k, v in data.items()} diff --git a/pom.xml b/pom.xml index 029d5fa1da71..7e3785be228b 100644 --- a/pom.xml +++ b/pom.xml @@ -1056,6 +1056,9 @@ samples/client/petstore/typescript-angular-v6-provided-in-root samples/server/petstore/rust-server + samples/server/petstore/python-aiohttp + samples/server/petstore/flaskConnexion + samples/server/petstore/flaskConnexion-python2 diff --git a/samples/server/petstore/flaskConnexion-python2/Makefile b/samples/server/petstore/flaskConnexion-python2/Makefile new file mode 100644 index 000000000000..02f8acd53b4d --- /dev/null +++ b/samples/server/petstore/flaskConnexion-python2/Makefile @@ -0,0 +1,20 @@ + #!/bin/bash + +REQUIREMENTS_OUT=test-requirements.txt.log +SETUP_OUT=*.egg-info +VENV=.venv + +clean: + rm -rf $(REQUIREMENTS_OUT) + rm -rf $(SETUP_OUT) + rm -rf $(VENV) + rm -rf .tox + rm -rf .coverage + find . -name "*.py[oc]" -delete + find . -name "__pycache__" -delete + +test: clean + bash ./test_python2.sh + +test-all: clean + bash ./test_python2.sh diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/security_controller_.py b/samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/security_controller_.py new file mode 100644 index 000000000000..cbf0d187d4bc --- /dev/null +++ b/samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/security_controller_.py @@ -0,0 +1,26 @@ + +def info_from_api_key_api_key(api_key, required_scopes): + """ + Returned value will be available in request['token_info']. + 'sub' or 'uid' will be set in request['user']. + Should return None if api_key is invalid + """ + return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + + +def info_from_token_petstore_auth(token): + """ + Validate and decode token. + Returned value will be available in request['token_info']. + 'sub' or 'uid' will be set in request['user']. + 'scope' or 'scopes' will be passed to scope validation function. + Should return None if token is invalid + """ + return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + + +def validate_scope_petstore_auth(required_scopes, token_scopes): + """ Validate required scopes are included in token scope """ + return set(required_scopes).issubset(set(token_scopes)) + + diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/models/pet.py b/samples/server/petstore/flaskConnexion-python2/openapi_server/models/pet.py index e3742dad2207..a9c5e52330fc 100644 --- a/samples/server/petstore/flaskConnexion-python2/openapi_server/models/pet.py +++ b/samples/server/petstore/flaskConnexion-python2/openapi_server/models/pet.py @@ -6,6 +6,8 @@ from typing import List, Dict # noqa: F401 from openapi_server.models.base_model_ import Model +from openapi_server.models.category import Category +from openapi_server.models.tag import Tag from openapi_server import util from openapi_server.models.category import Category # noqa: E501 diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/openapi/openapi.yaml b/samples/server/petstore/flaskConnexion-python2/openapi_server/openapi/openapi.yaml index f0cc6c9fdd48..37c1cee4d715 100644 --- a/samples/server/petstore/flaskConnexion-python2/openapi_server/openapi/openapi.yaml +++ b/samples/server/petstore/flaskConnexion-python2/openapi_server/openapi/openapi.yaml @@ -159,7 +159,7 @@ paths: tags: - pet x-openapi-router-controller: openapi_server.controllers.pet_controller - /pet/{petId}: + /pet/{pet_id}: delete: operationId: delete_pet parameters: @@ -169,7 +169,7 @@ paths: type: string - description: Pet id to delete in: path - name: petId + name: pet_id required: true schema: format: int64 @@ -192,7 +192,7 @@ paths: parameters: - description: ID of pet to return in: path - name: petId + name: pet_id required: true schema: format: int64 @@ -224,7 +224,7 @@ paths: parameters: - description: ID of pet that needs to be updated in: path - name: petId + name: pet_id required: true schema: format: int64 @@ -252,13 +252,13 @@ paths: tags: - pet x-openapi-router-controller: openapi_server.controllers.pet_controller - /pet/{petId}/uploadImage: + /pet/{pet_id}/uploadImage: post: operationId: upload_file parameters: - description: ID of pet to update in: path - name: petId + name: pet_id required: true schema: format: int64 @@ -338,14 +338,14 @@ paths: - store x-codegen-request-body-name: body x-openapi-router-controller: openapi_server.controllers.store_controller - /store/order/{orderId}: + /store/order/{order_id}: delete: description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors operationId: delete_order parameters: - description: ID of the order that needs to be deleted in: path - name: orderId + name: order_id required: true schema: type: string @@ -366,7 +366,7 @@ paths: parameters: - description: ID of pet that needs to be fetched in: path - name: orderId + name: order_id required: true schema: format: int64 @@ -772,7 +772,10 @@ components: write:pets: modify pets in your account read:pets: read your pets type: oauth2 + x-tokenInfoFunc: openapi_server.controllers.security_controller_.info_from_petstore_auth + x-scopeValidateFunc: openapi_server.controllers.security_controller_.validate_scope_petstore_auth api_key: in: header name: api_key type: apiKey + x-apikeyInfoFunc: openapi_server.controllers.security_controller_.info_from_api_key diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_pet_controller.py b/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_pet_controller.py index 56d0dc2b57c5..230ac4a4b607 100644 --- a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_pet_controller.py +++ b/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_pet_controller.py @@ -1,6 +1,7 @@ # coding: utf-8 from __future__ import absolute_import +import unittest from flask import json from six import BytesIO @@ -13,16 +14,38 @@ class TestPetController(BaseTestCase): """PetController integration test stubs""" + @unittest.skip("Connexion does not support multiple consummes. See https://github.com/zalando/connexion/pull/760") def test_add_pet(self): """Test case for add_pet Add a new pet to the store """ - body = Pet() + pet = { + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" +} + headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer special-key', + } response = self.client.open( '/v2/pet', method='POST', - data=json.dumps(body), + headers=headers, + data=json.dumps(pet), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -32,9 +55,12 @@ def test_delete_pet(self): Deletes a pet """ - headers = [('api_key', 'api_key_example')] + headers = { + 'api_key': 'api_key_example', + 'Authorization': 'Bearer special-key', + } response = self.client.open( - '/v2/pet/{petId}'.format(pet_id=789), + '/v2/pet/{pet_id}'.format(pet_id=789), method='DELETE', headers=headers) self.assert200(response, @@ -46,9 +72,14 @@ def test_find_pets_by_status(self): Finds Pets by status """ query_string = [('status', 'available')] + headers = { + 'Accept': 'application/json', + 'Authorization': 'Bearer special-key', + } response = self.client.open( '/v2/pet/findByStatus', method='GET', + headers=headers, query_string=query_string) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -59,9 +90,14 @@ def test_find_pets_by_tags(self): Finds Pets by tags """ query_string = [('tags', 'tags_example')] + headers = { + 'Accept': 'application/json', + 'Authorization': 'Bearer special-key', + } response = self.client.open( '/v2/pet/findByTags', method='GET', + headers=headers, query_string=query_string) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -71,51 +107,91 @@ def test_get_pet_by_id(self): Find pet by ID """ + headers = { + 'Accept': 'application/json', + 'api_key': 'special-key', + } response = self.client.open( - '/v2/pet/{petId}'.format(pet_id=789), - method='GET') + '/v2/pet/{pet_id}'.format(pet_id=789), + method='GET', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("Connexion does not support multiple consummes. See https://github.com/zalando/connexion/pull/760") def test_update_pet(self): """Test case for update_pet Update an existing pet """ - body = Pet() + pet = { + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" +} + headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer special-key', + } response = self.client.open( '/v2/pet', method='PUT', - data=json.dumps(body), + headers=headers, + data=json.dumps(pet), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("application/x-www-form-urlencoded not supported by Connexion") def test_update_pet_with_form(self): """Test case for update_pet_with_form Updates a pet in the store with form data """ + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'Bearer special-key', + } data = dict(name='name_example', status='status_example') response = self.client.open( - '/v2/pet/{petId}'.format(pet_id=789), + '/v2/pet/{pet_id}'.format(pet_id=789), method='POST', + headers=headers, data=data, content_type='application/x-www-form-urlencoded') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("multipart/form-data not supported by Connexion") def test_upload_file(self): """Test case for upload_file uploads an image """ + headers = { + 'Accept': 'application/json', + 'Content-Type': 'multipart/form-data', + 'Authorization': 'Bearer special-key', + } data = dict(additional_metadata='additional_metadata_example', file=(BytesIO(b'some file data'), 'file.txt')) response = self.client.open( - '/v2/pet/{petId}/uploadImage'.format(pet_id=789), + '/v2/pet/{pet_id}/uploadImage'.format(pet_id=789), method='POST', + headers=headers, data=data, content_type='multipart/form-data') self.assert200(response, @@ -123,5 +199,4 @@ def test_upload_file(self): if __name__ == '__main__': - import unittest unittest.main() diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_store_controller.py b/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_store_controller.py index 05749b7b507f..ea8c95b20ef7 100644 --- a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_store_controller.py +++ b/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_store_controller.py @@ -1,6 +1,7 @@ # coding: utf-8 from __future__ import absolute_import +import unittest from flask import json from six import BytesIO @@ -17,9 +18,12 @@ def test_delete_order(self): Delete purchase order by ID """ + headers = { + } response = self.client.open( - '/v2/store/order/{orderId}'.format(order_id='order_id_example'), - method='DELETE') + '/v2/store/order/{order_id}'.format(order_id='order_id_example'), + method='DELETE', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -28,9 +32,14 @@ def test_get_inventory(self): Returns pet inventories by status """ + headers = { + 'Accept': 'application/json', + 'api_key': 'special-key', + } response = self.client.open( '/v2/store/inventory', - method='GET') + method='GET', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -39,27 +48,36 @@ def test_get_order_by_id(self): Find purchase order by ID """ + headers = { + 'Accept': 'application/json', + } response = self.client.open( - '/v2/store/order/{orderId}'.format(order_id=5), - method='GET') + '/v2/store/order/{order_id}'.format(order_id=5), + method='GET', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") def test_place_order(self): """Test case for place_order Place an order for a pet """ - body = Order() + order = {} + headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + } response = self.client.open( '/v2/store/order', method='POST', - data=json.dumps(body), + headers=headers, + data=json.dumps(order), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) if __name__ == '__main__': - import unittest unittest.main() diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_user_controller.py b/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_user_controller.py index dac3b779b96f..d1d970eec6dc 100644 --- a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_user_controller.py +++ b/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_user_controller.py @@ -1,6 +1,7 @@ # coding: utf-8 from __future__ import absolute_import +import unittest from flask import json from six import BytesIO @@ -12,44 +13,59 @@ class TestUserController(BaseTestCase): """UserController integration test stubs""" + @unittest.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") def test_create_user(self): """Test case for create_user Create user """ - body = User() + user = {} + headers = { + 'Content-Type': 'application/json', + } response = self.client.open( '/v2/user', method='POST', - data=json.dumps(body), + headers=headers, + data=json.dumps(user), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") def test_create_users_with_array_input(self): """Test case for create_users_with_array_input Creates list of users with given input array """ - body = None + user = [] + headers = { + 'Content-Type': 'application/json', + } response = self.client.open( '/v2/user/createWithArray', method='POST', - data=json.dumps(body), + headers=headers, + data=json.dumps(user), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") def test_create_users_with_list_input(self): """Test case for create_users_with_list_input Creates list of users with given input array """ - body = None + user = [] + headers = { + 'Content-Type': 'application/json', + } response = self.client.open( '/v2/user/createWithList', method='POST', - data=json.dumps(body), + headers=headers, + data=json.dumps(user), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -59,9 +75,12 @@ def test_delete_user(self): Delete user """ + headers = { + } response = self.client.open( '/v2/user/{username}'.format(username='username_example'), - method='DELETE') + method='DELETE', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -70,9 +89,13 @@ def test_get_user_by_name(self): Get user by user name """ + headers = { + 'Accept': 'application/json', + } response = self.client.open( '/v2/user/{username}'.format(username='username_example'), - method='GET') + method='GET', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -83,9 +106,13 @@ def test_login_user(self): """ query_string = [('username', 'username_example'), ('password', 'password_example')] + headers = { + 'Accept': 'application/json', + } response = self.client.open( '/v2/user/login', method='GET', + headers=headers, query_string=query_string) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -95,27 +122,34 @@ def test_logout_user(self): Logs out current logged in user session """ + headers = { + } response = self.client.open( '/v2/user/logout', - method='GET') + method='GET', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") def test_update_user(self): """Test case for update_user Updated user """ - body = User() + user = {} + headers = { + 'Content-Type': 'application/json', + } response = self.client.open( '/v2/user/{username}'.format(username='username_example'), method='PUT', - data=json.dumps(body), + headers=headers, + data=json.dumps(user), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) if __name__ == '__main__': - import unittest unittest.main() diff --git a/samples/server/petstore/flaskConnexion-python2/pom.xml b/samples/server/petstore/flaskConnexion-python2/pom.xml new file mode 100644 index 000000000000..04b4d23a1be7 --- /dev/null +++ b/samples/server/petstore/flaskConnexion-python2/pom.xml @@ -0,0 +1,46 @@ + + 4.0.0 + org.openapitools + FlaskConnexionTestsPython2 + pom + 1.0-SNAPSHOT + Python Flask Server python 2 + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory} + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + nose-test + integration-test + + exec + + + make + + test-all + + + + + + + + diff --git a/samples/server/petstore/flaskConnexion-python2/requirements.txt b/samples/server/petstore/flaskConnexion-python2/requirements.txt index c764c056b3b6..de4b8821af6b 100644 --- a/samples/server/petstore/flaskConnexion-python2/requirements.txt +++ b/samples/server/petstore/flaskConnexion-python2/requirements.txt @@ -1,4 +1,4 @@ -connexion == 2.0.0 +connexion == 2.0.2 swagger-ui-bundle == 0.0.2 python_dateutil == 2.6.0 typing == 3.5.2.2 diff --git a/samples/server/petstore/flaskConnexion-python2/test_python2.sh b/samples/server/petstore/flaskConnexion-python2/test_python2.sh new file mode 100755 index 000000000000..e579f4d7f559 --- /dev/null +++ b/samples/server/petstore/flaskConnexion-python2/test_python2.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +REQUIREMENTS_FILE=test-requirements.txt +REQUIREMENTS_OUT=test-requirements.txt.log +SETUP_OUT=*.egg-info +VENV=.venv +DEACTIVE=false + +export LC_ALL=en_US.UTF-8 +export LANG=en_US.UTF-8 + +### set virtualenv +if [ -z "$VIRTUAL_ENV" ]; then + virtualenv $VENV --no-site-packages --always-copy --python python + source $VENV/bin/activate + DEACTIVE=true +fi + +### install dependencies +pip install -r $REQUIREMENTS_FILE | tee -a $REQUIREMENTS_OUT +python setup.py develop + +### run tests +tox || exit 1 + +### static analysis of code +flake8 --show-source petstore_api/ + +### deactivate virtualenv +#if [ $DEACTIVE == true ]; then +# deactivate +#fi diff --git a/samples/server/petstore/flaskConnexion-python2/tox.ini b/samples/server/petstore/flaskConnexion-python2/tox.ini index b633a1f05980..26985414c882 100644 --- a/samples/server/petstore/flaskConnexion-python2/tox.ini +++ b/samples/server/petstore/flaskConnexion-python2/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27, py35 +envlist = py27, py3 [testenv] deps=-r{toxinidir}/requirements.txt diff --git a/samples/server/petstore/flaskConnexion/Makefile b/samples/server/petstore/flaskConnexion/Makefile new file mode 100644 index 000000000000..b32712ef06c3 --- /dev/null +++ b/samples/server/petstore/flaskConnexion/Makefile @@ -0,0 +1,20 @@ + #!/bin/bash + +REQUIREMENTS_OUT=test-requirements.txt.log +SETUP_OUT=*.egg-info +VENV=.venv + +clean: + rm -rf $(REQUIREMENTS_OUT) + rm -rf $(SETUP_OUT) + rm -rf $(VENV) + rm -rf .tox + rm -rf .coverage + find . -name "*.py[oc]" -delete + find . -name "__pycache__" -delete + +test: clean + bash ./test_python3.sh + +test-all: clean + bash ./test_python3.sh diff --git a/samples/server/petstore/flaskConnexion/openapi_server/controllers/security_controller_.py b/samples/server/petstore/flaskConnexion/openapi_server/controllers/security_controller_.py new file mode 100644 index 000000000000..cbf0d187d4bc --- /dev/null +++ b/samples/server/petstore/flaskConnexion/openapi_server/controllers/security_controller_.py @@ -0,0 +1,26 @@ + +def info_from_api_key_api_key(api_key, required_scopes): + """ + Returned value will be available in request['token_info']. + 'sub' or 'uid' will be set in request['user']. + Should return None if api_key is invalid + """ + return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + + +def info_from_token_petstore_auth(token): + """ + Validate and decode token. + Returned value will be available in request['token_info']. + 'sub' or 'uid' will be set in request['user']. + 'scope' or 'scopes' will be passed to scope validation function. + Should return None if token is invalid + """ + return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + + +def validate_scope_petstore_auth(required_scopes, token_scopes): + """ Validate required scopes are included in token scope """ + return set(required_scopes).issubset(set(token_scopes)) + + diff --git a/samples/server/petstore/flaskConnexion/openapi_server/models/pet.py b/samples/server/petstore/flaskConnexion/openapi_server/models/pet.py index 3489b25d5bcb..e61674165e1c 100644 --- a/samples/server/petstore/flaskConnexion/openapi_server/models/pet.py +++ b/samples/server/petstore/flaskConnexion/openapi_server/models/pet.py @@ -6,6 +6,8 @@ from typing import List, Dict # noqa: F401 from openapi_server.models.base_model_ import Model +from openapi_server.models.category import Category +from openapi_server.models.tag import Tag from openapi_server import util from openapi_server.models.category import Category # noqa: E501 diff --git a/samples/server/petstore/flaskConnexion/openapi_server/openapi/openapi.yaml b/samples/server/petstore/flaskConnexion/openapi_server/openapi/openapi.yaml index f0cc6c9fdd48..37c1cee4d715 100644 --- a/samples/server/petstore/flaskConnexion/openapi_server/openapi/openapi.yaml +++ b/samples/server/petstore/flaskConnexion/openapi_server/openapi/openapi.yaml @@ -159,7 +159,7 @@ paths: tags: - pet x-openapi-router-controller: openapi_server.controllers.pet_controller - /pet/{petId}: + /pet/{pet_id}: delete: operationId: delete_pet parameters: @@ -169,7 +169,7 @@ paths: type: string - description: Pet id to delete in: path - name: petId + name: pet_id required: true schema: format: int64 @@ -192,7 +192,7 @@ paths: parameters: - description: ID of pet to return in: path - name: petId + name: pet_id required: true schema: format: int64 @@ -224,7 +224,7 @@ paths: parameters: - description: ID of pet that needs to be updated in: path - name: petId + name: pet_id required: true schema: format: int64 @@ -252,13 +252,13 @@ paths: tags: - pet x-openapi-router-controller: openapi_server.controllers.pet_controller - /pet/{petId}/uploadImage: + /pet/{pet_id}/uploadImage: post: operationId: upload_file parameters: - description: ID of pet to update in: path - name: petId + name: pet_id required: true schema: format: int64 @@ -338,14 +338,14 @@ paths: - store x-codegen-request-body-name: body x-openapi-router-controller: openapi_server.controllers.store_controller - /store/order/{orderId}: + /store/order/{order_id}: delete: description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors operationId: delete_order parameters: - description: ID of the order that needs to be deleted in: path - name: orderId + name: order_id required: true schema: type: string @@ -366,7 +366,7 @@ paths: parameters: - description: ID of pet that needs to be fetched in: path - name: orderId + name: order_id required: true schema: format: int64 @@ -772,7 +772,10 @@ components: write:pets: modify pets in your account read:pets: read your pets type: oauth2 + x-tokenInfoFunc: openapi_server.controllers.security_controller_.info_from_petstore_auth + x-scopeValidateFunc: openapi_server.controllers.security_controller_.validate_scope_petstore_auth api_key: in: header name: api_key type: apiKey + x-apikeyInfoFunc: openapi_server.controllers.security_controller_.info_from_api_key diff --git a/samples/server/petstore/flaskConnexion/openapi_server/test/test_pet_controller.py b/samples/server/petstore/flaskConnexion/openapi_server/test/test_pet_controller.py index c3652a2df3ce..c756d7bb2ca1 100644 --- a/samples/server/petstore/flaskConnexion/openapi_server/test/test_pet_controller.py +++ b/samples/server/petstore/flaskConnexion/openapi_server/test/test_pet_controller.py @@ -1,6 +1,7 @@ # coding: utf-8 from __future__ import absolute_import +import unittest from flask import json from six import BytesIO @@ -13,16 +14,38 @@ class TestPetController(BaseTestCase): """PetController integration test stubs""" + @unittest.skip("Connexion does not support multiple consummes. See https://github.com/zalando/connexion/pull/760") def test_add_pet(self): """Test case for add_pet Add a new pet to the store """ - body = Pet() + pet = { + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" +} + headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer special-key', + } response = self.client.open( '/v2/pet', method='POST', - data=json.dumps(body), + headers=headers, + data=json.dumps(pet), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -32,9 +55,12 @@ def test_delete_pet(self): Deletes a pet """ - headers = [('api_key', 'api_key_example')] + headers = { + 'api_key': 'api_key_example', + 'Authorization': 'Bearer special-key', + } response = self.client.open( - '/v2/pet/{petId}'.format(pet_id=56), + '/v2/pet/{pet_id}'.format(pet_id=56), method='DELETE', headers=headers) self.assert200(response, @@ -46,9 +72,14 @@ def test_find_pets_by_status(self): Finds Pets by status """ query_string = [('status', 'available')] + headers = { + 'Accept': 'application/json', + 'Authorization': 'Bearer special-key', + } response = self.client.open( '/v2/pet/findByStatus', method='GET', + headers=headers, query_string=query_string) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -59,9 +90,14 @@ def test_find_pets_by_tags(self): Finds Pets by tags """ query_string = [('tags', 'tags_example')] + headers = { + 'Accept': 'application/json', + 'Authorization': 'Bearer special-key', + } response = self.client.open( '/v2/pet/findByTags', method='GET', + headers=headers, query_string=query_string) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -71,51 +107,91 @@ def test_get_pet_by_id(self): Find pet by ID """ + headers = { + 'Accept': 'application/json', + 'api_key': 'special-key', + } response = self.client.open( - '/v2/pet/{petId}'.format(pet_id=56), - method='GET') + '/v2/pet/{pet_id}'.format(pet_id=56), + method='GET', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("Connexion does not support multiple consummes. See https://github.com/zalando/connexion/pull/760") def test_update_pet(self): """Test case for update_pet Update an existing pet """ - body = Pet() + pet = { + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" +} + headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer special-key', + } response = self.client.open( '/v2/pet', method='PUT', - data=json.dumps(body), + headers=headers, + data=json.dumps(pet), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("application/x-www-form-urlencoded not supported by Connexion") def test_update_pet_with_form(self): """Test case for update_pet_with_form Updates a pet in the store with form data """ + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'Bearer special-key', + } data = dict(name='name_example', status='status_example') response = self.client.open( - '/v2/pet/{petId}'.format(pet_id=56), + '/v2/pet/{pet_id}'.format(pet_id=56), method='POST', + headers=headers, data=data, content_type='application/x-www-form-urlencoded') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("multipart/form-data not supported by Connexion") def test_upload_file(self): """Test case for upload_file uploads an image """ + headers = { + 'Accept': 'application/json', + 'Content-Type': 'multipart/form-data', + 'Authorization': 'Bearer special-key', + } data = dict(additional_metadata='additional_metadata_example', file=(BytesIO(b'some file data'), 'file.txt')) response = self.client.open( - '/v2/pet/{petId}/uploadImage'.format(pet_id=56), + '/v2/pet/{pet_id}/uploadImage'.format(pet_id=56), method='POST', + headers=headers, data=data, content_type='multipart/form-data') self.assert200(response, @@ -123,5 +199,4 @@ def test_upload_file(self): if __name__ == '__main__': - import unittest unittest.main() diff --git a/samples/server/petstore/flaskConnexion/openapi_server/test/test_store_controller.py b/samples/server/petstore/flaskConnexion/openapi_server/test/test_store_controller.py index 05749b7b507f..ea8c95b20ef7 100644 --- a/samples/server/petstore/flaskConnexion/openapi_server/test/test_store_controller.py +++ b/samples/server/petstore/flaskConnexion/openapi_server/test/test_store_controller.py @@ -1,6 +1,7 @@ # coding: utf-8 from __future__ import absolute_import +import unittest from flask import json from six import BytesIO @@ -17,9 +18,12 @@ def test_delete_order(self): Delete purchase order by ID """ + headers = { + } response = self.client.open( - '/v2/store/order/{orderId}'.format(order_id='order_id_example'), - method='DELETE') + '/v2/store/order/{order_id}'.format(order_id='order_id_example'), + method='DELETE', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -28,9 +32,14 @@ def test_get_inventory(self): Returns pet inventories by status """ + headers = { + 'Accept': 'application/json', + 'api_key': 'special-key', + } response = self.client.open( '/v2/store/inventory', - method='GET') + method='GET', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -39,27 +48,36 @@ def test_get_order_by_id(self): Find purchase order by ID """ + headers = { + 'Accept': 'application/json', + } response = self.client.open( - '/v2/store/order/{orderId}'.format(order_id=5), - method='GET') + '/v2/store/order/{order_id}'.format(order_id=5), + method='GET', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") def test_place_order(self): """Test case for place_order Place an order for a pet """ - body = Order() + order = {} + headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + } response = self.client.open( '/v2/store/order', method='POST', - data=json.dumps(body), + headers=headers, + data=json.dumps(order), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) if __name__ == '__main__': - import unittest unittest.main() diff --git a/samples/server/petstore/flaskConnexion/openapi_server/test/test_user_controller.py b/samples/server/petstore/flaskConnexion/openapi_server/test/test_user_controller.py index dac3b779b96f..d1d970eec6dc 100644 --- a/samples/server/petstore/flaskConnexion/openapi_server/test/test_user_controller.py +++ b/samples/server/petstore/flaskConnexion/openapi_server/test/test_user_controller.py @@ -1,6 +1,7 @@ # coding: utf-8 from __future__ import absolute_import +import unittest from flask import json from six import BytesIO @@ -12,44 +13,59 @@ class TestUserController(BaseTestCase): """UserController integration test stubs""" + @unittest.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") def test_create_user(self): """Test case for create_user Create user """ - body = User() + user = {} + headers = { + 'Content-Type': 'application/json', + } response = self.client.open( '/v2/user', method='POST', - data=json.dumps(body), + headers=headers, + data=json.dumps(user), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") def test_create_users_with_array_input(self): """Test case for create_users_with_array_input Creates list of users with given input array """ - body = None + user = [] + headers = { + 'Content-Type': 'application/json', + } response = self.client.open( '/v2/user/createWithArray', method='POST', - data=json.dumps(body), + headers=headers, + data=json.dumps(user), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") def test_create_users_with_list_input(self): """Test case for create_users_with_list_input Creates list of users with given input array """ - body = None + user = [] + headers = { + 'Content-Type': 'application/json', + } response = self.client.open( '/v2/user/createWithList', method='POST', - data=json.dumps(body), + headers=headers, + data=json.dumps(user), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -59,9 +75,12 @@ def test_delete_user(self): Delete user """ + headers = { + } response = self.client.open( '/v2/user/{username}'.format(username='username_example'), - method='DELETE') + method='DELETE', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -70,9 +89,13 @@ def test_get_user_by_name(self): Get user by user name """ + headers = { + 'Accept': 'application/json', + } response = self.client.open( '/v2/user/{username}'.format(username='username_example'), - method='GET') + method='GET', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -83,9 +106,13 @@ def test_login_user(self): """ query_string = [('username', 'username_example'), ('password', 'password_example')] + headers = { + 'Accept': 'application/json', + } response = self.client.open( '/v2/user/login', method='GET', + headers=headers, query_string=query_string) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -95,27 +122,34 @@ def test_logout_user(self): Logs out current logged in user session """ + headers = { + } response = self.client.open( '/v2/user/logout', - method='GET') + method='GET', + headers=headers) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + @unittest.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") def test_update_user(self): """Test case for update_user Updated user """ - body = User() + user = {} + headers = { + 'Content-Type': 'application/json', + } response = self.client.open( '/v2/user/{username}'.format(username='username_example'), method='PUT', - data=json.dumps(body), + headers=headers, + data=json.dumps(user), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) if __name__ == '__main__': - import unittest unittest.main() diff --git a/samples/server/petstore/flaskConnexion/pom.xml b/samples/server/petstore/flaskConnexion/pom.xml new file mode 100644 index 000000000000..2fee39a223d5 --- /dev/null +++ b/samples/server/petstore/flaskConnexion/pom.xml @@ -0,0 +1,46 @@ + + 4.0.0 + org.openapitools + FlaskConnexionTests + pom + 1.0-SNAPSHOT + Python Flask Server + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory} + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + nose-test + integration-test + + exec + + + make + + test-all + + + + + + + + diff --git a/samples/server/petstore/flaskConnexion/requirements.txt b/samples/server/petstore/flaskConnexion/requirements.txt index 52aca69de9ac..66ecf7c26851 100644 --- a/samples/server/petstore/flaskConnexion/requirements.txt +++ b/samples/server/petstore/flaskConnexion/requirements.txt @@ -1,4 +1,4 @@ -connexion == 2.0.0 +connexion == 2.0.2 swagger-ui-bundle == 0.0.2 python_dateutil == 2.6.0 setuptools >= 21.0.0 diff --git a/samples/server/petstore/flaskConnexion/test_python3.sh b/samples/server/petstore/flaskConnexion/test_python3.sh new file mode 100755 index 000000000000..9bd589401cd3 --- /dev/null +++ b/samples/server/petstore/flaskConnexion/test_python3.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +REQUIREMENTS_FILE=test-requirements.txt +REQUIREMENTS_OUT=test-requirements.txt.log +SETUP_OUT=*.egg-info +VENV=.venv +DEACTIVE=false + +export LC_ALL=en_US.UTF-8 +export LANG=en_US.UTF-8 + +### set virtualenv +if [ -z "$VIRTUAL_ENV" ]; then + virtualenv $VENV --no-site-packages --always-copy --python python3 + source $VENV/bin/activate + DEACTIVE=true +fi + +### install dependencies +pip install -r $REQUIREMENTS_FILE | tee -a $REQUIREMENTS_OUT +python setup.py develop + +### run tests +tox || exit 1 + +### static analysis of code +flake8 --show-source petstore_api/ + +### deactivate virtualenv +#if [ $DEACTIVE == true ]; then +# deactivate +#fi diff --git a/samples/server/petstore/flaskConnexion/tox.ini b/samples/server/petstore/flaskConnexion/tox.ini index 3e0b644eec48..ab4dfbb81b8b 100644 --- a/samples/server/petstore/flaskConnexion/tox.ini +++ b/samples/server/petstore/flaskConnexion/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py35 +envlist = py3 [testenv] deps=-r{toxinidir}/requirements.txt diff --git a/samples/server/petstore/python-aiohttp/.openapi-generator-ignore b/samples/server/petstore/python-aiohttp/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/petstore/python-aiohttp/.openapi-generator/VERSION b/samples/server/petstore/python-aiohttp/.openapi-generator/VERSION new file mode 100644 index 000000000000..afa636560641 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/.openapi-generator/VERSION @@ -0,0 +1 @@ +4.0.0-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/python-aiohttp/Makefile b/samples/server/petstore/python-aiohttp/Makefile new file mode 100644 index 000000000000..763fe2bc4a45 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/Makefile @@ -0,0 +1,18 @@ + #!/bin/bash + +REQUIREMENTS_OUT=requirements.txt.log +VENV=.venv + +clean: + rm -rf $(REQUIREMENTS_OUT) + rm -rf $(VENV) + rm -rf .pytest_cache + rm -rf .coverage + find . -name "*.py[oc]" -delete + find . -name "__pycache__" -delete + +test: clean + bash ./test_python3.sh + +test-all: clean + bash ./test_python3.sh diff --git a/samples/server/petstore/python-aiohttp/README.md b/samples/server/petstore/python-aiohttp/README.md new file mode 100644 index 000000000000..5983d83f1848 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/README.md @@ -0,0 +1,46 @@ +# OpenAPI generated server + +## Overview +This server was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the +[OpenAPI-Spec](https://openapis.org) from a remote server, you can easily generate a server stub. This +is an example of building a OpenAPI-enabled aiohtpp server. + +This example uses the [Connexion](https://github.com/zalando/connexion) library on top of aiohtpp. + +## Requirements +Python 3.5.2+ + +## Usage +To run the server, please execute the following from the root directory: + +``` +pip3 install -r requirements.txt +python3 -m openapi_server +``` + +and open your browser to here: + +``` +http://localhost:8080/v2/ui/ +``` + +Your OpenAPI definition lives here: + +``` +http://localhost:8080/v2/openapi.json +``` + +To launch the integration tests, use pytest: +``` +sudo pip install -r test-requirements.txt +pytest +``` + +## Prevent file overriding + +After first generation, add edited files to _.openapi-generator-ignore_ to prevent generator to overwrite them. Typically: +``` +server/controllers/* +test/* +*.txt +``` diff --git a/samples/server/petstore/python-aiohttp/openapi_server/__main__.py b/samples/server/petstore/python-aiohttp/openapi_server/__main__.py new file mode 100644 index 000000000000..f20ae81db34e --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/__main__.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 + +from . import main + +if __name__ == '__main__': + main() diff --git a/samples/server/petstore/python-aiohttp/openapi_server/controllers/__init__.py b/samples/server/petstore/python-aiohttp/openapi_server/controllers/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/samples/server/petstore/python-aiohttp/openapi_server/controllers/pet_controller.py b/samples/server/petstore/python-aiohttp/openapi_server/controllers/pet_controller.py new file mode 100644 index 000000000000..ad7557832bab --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/controllers/pet_controller.py @@ -0,0 +1,114 @@ +from typing import List, Dict +from aiohttp import web + +from openapi_server.models.api_response import ApiResponse +from openapi_server.models.pet import Pet +from openapi_server import util + + +async def add_pet(request: web.Request, body) -> web.Response: + """Add a new pet to the store + + + + :param body: Pet object that needs to be added to the store + :type body: dict | bytes + + """ + body = Pet.from_dict(body) + return web.Response(status=200) + + +async def delete_pet(request: web.Request, pet_id, api_key=None) -> web.Response: + """Deletes a pet + + + + :param pet_id: Pet id to delete + :type pet_id: int + :param api_key: + :type api_key: str + + """ + return web.Response(status=200) + + +async def find_pets_by_status(request: web.Request, status) -> web.Response: + """Finds Pets by status + + Multiple status values can be provided with comma separated strings + + :param status: Status values that need to be considered for filter + :type status: List[str] + + """ + return web.Response(status=200) + + +async def find_pets_by_tags(request: web.Request, tags) -> web.Response: + """Finds Pets by tags + + Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + + :param tags: Tags to filter by + :type tags: List[str] + + """ + return web.Response(status=200) + + +async def get_pet_by_id(request: web.Request, pet_id) -> web.Response: + """Find pet by ID + + Returns a single pet + + :param pet_id: ID of pet to return + :type pet_id: int + + """ + return web.Response(status=200) + + +async def update_pet(request: web.Request, body) -> web.Response: + """Update an existing pet + + + + :param body: Pet object that needs to be added to the store + :type body: dict | bytes + + """ + body = Pet.from_dict(body) + return web.Response(status=200) + + +async def update_pet_with_form(request: web.Request, pet_id, name=None, status=None) -> web.Response: + """Updates a pet in the store with form data + + + + :param pet_id: ID of pet that needs to be updated + :type pet_id: int + :param name: Updated name of the pet + :type name: str + :param status: Updated status of the pet + :type status: str + + """ + return web.Response(status=200) + + +async def upload_file(request: web.Request, pet_id, additional_metadata=None, file=None) -> web.Response: + """uploads an image + + + + :param pet_id: ID of pet to update + :type pet_id: int + :param additional_metadata: Additional data to pass to server + :type additional_metadata: str + :param file: file to upload + :type file: str + + """ + return web.Response(status=200) diff --git a/samples/server/petstore/python-aiohttp/openapi_server/controllers/security_controller_.py b/samples/server/petstore/python-aiohttp/openapi_server/controllers/security_controller_.py new file mode 100644 index 000000000000..5815dd85d4a3 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/controllers/security_controller_.py @@ -0,0 +1,29 @@ +from typing import List + + +def info_from_api_key(api_key: str, required_scopes: None) -> dict: + """ + Check and retrieve authentication information from api_key. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. + Should return None if api_key is invalid + """ + return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + + +def info_from_petstore_auth(token: str) -> dict: + """ + Validate and decode token. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. + 'scope' or 'scopes' will be passed to scope validation function. + Should return None if token is invalid + """ + return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + + +def validate_scope_petstore_auth(required_scopes: List[str], token_scopes: List[str]) -> bool: + """ Validate required scopes are included in token scope """ + return set(required_scopes).issubset(set(token_scopes)) + + diff --git a/samples/server/petstore/python-aiohttp/openapi_server/controllers/store_controller.py b/samples/server/petstore/python-aiohttp/openapi_server/controllers/store_controller.py new file mode 100644 index 000000000000..80512d357f26 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/controllers/store_controller.py @@ -0,0 +1,52 @@ +from typing import List, Dict +from aiohttp import web + +from openapi_server.models.order import Order +from openapi_server import util + + +async def delete_order(request: web.Request, order_id) -> web.Response: + """Delete purchase order by ID + + For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + + :param order_id: ID of the order that needs to be deleted + :type order_id: str + + """ + return web.Response(status=200) + + +async def get_inventory(request: web.Request, ) -> web.Response: + """Returns pet inventories by status + + Returns a map of status codes to quantities + + + """ + return web.Response(status=200) + + +async def get_order_by_id(request: web.Request, order_id) -> web.Response: + """Find purchase order by ID + + For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions + + :param order_id: ID of pet that needs to be fetched + :type order_id: int + + """ + return web.Response(status=200) + + +async def place_order(request: web.Request, body) -> web.Response: + """Place an order for a pet + + + + :param body: order placed for purchasing the pet + :type body: dict | bytes + + """ + body = Order.from_dict(body) + return web.Response(status=200) diff --git a/samples/server/petstore/python-aiohttp/openapi_server/controllers/user_controller.py b/samples/server/petstore/python-aiohttp/openapi_server/controllers/user_controller.py new file mode 100644 index 000000000000..89dd08724136 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/controllers/user_controller.py @@ -0,0 +1,107 @@ +from typing import List, Dict +from aiohttp import web + +from openapi_server.models.user import User +from openapi_server import util + + +async def create_user(request: web.Request, body) -> web.Response: + """Create user + + This can only be done by the logged in user. + + :param body: Created user object + :type body: dict | bytes + + """ + body = User.from_dict(body) + return web.Response(status=200) + + +async def create_users_with_array_input(request: web.Request, body) -> web.Response: + """Creates list of users with given input array + + + + :param body: List of user object + :type body: list | bytes + + """ + body = [User.from_dict(d) for d in body] + return web.Response(status=200) + + +async def create_users_with_list_input(request: web.Request, body) -> web.Response: + """Creates list of users with given input array + + + + :param body: List of user object + :type body: list | bytes + + """ + body = [User.from_dict(d) for d in body] + return web.Response(status=200) + + +async def delete_user(request: web.Request, username) -> web.Response: + """Delete user + + This can only be done by the logged in user. + + :param username: The name that needs to be deleted + :type username: str + + """ + return web.Response(status=200) + + +async def get_user_by_name(request: web.Request, username) -> web.Response: + """Get user by user name + + + + :param username: The name that needs to be fetched. Use user1 for testing. + :type username: str + + """ + return web.Response(status=200) + + +async def login_user(request: web.Request, username, password) -> web.Response: + """Logs user into the system + + + + :param username: The user name for login + :type username: str + :param password: The password for login in clear text + :type password: str + + """ + return web.Response(status=200) + + +async def logout_user(request: web.Request, ) -> web.Response: + """Logs out current logged in user session + + + + + """ + return web.Response(status=200) + + +async def update_user(request: web.Request, username, body) -> web.Response: + """Updated user + + This can only be done by the logged in user. + + :param username: name that need to be deleted + :type username: str + :param body: Updated user object + :type body: dict | bytes + + """ + body = User.from_dict(body) + return web.Response(status=200) diff --git a/samples/server/petstore/python-aiohttp/openapi_server/models/__init__.py b/samples/server/petstore/python-aiohttp/openapi_server/models/__init__.py new file mode 100644 index 000000000000..8b108a5fe89a --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/models/__init__.py @@ -0,0 +1,9 @@ +# coding: utf-8 + +# import models into model package +from openapi_server.models.api_response import ApiResponse +from openapi_server.models.category import Category +from openapi_server.models.order import Order +from openapi_server.models.pet import Pet +from openapi_server.models.tag import Tag +from openapi_server.models.user import User diff --git a/samples/server/petstore/python-aiohttp/openapi_server/models/api_response.py b/samples/server/petstore/python-aiohttp/openapi_server/models/api_response.py new file mode 100644 index 000000000000..2fc7342b8bc2 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/models/api_response.py @@ -0,0 +1,110 @@ +# coding: utf-8 + +from datetime import date, datetime + +from typing import List, Dict, Type + +from openapi_server.models.base_model_ import Model +from openapi_server import util + + +class ApiResponse(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, code: int=None, type: str=None, message: str=None): + """ApiResponse - a model defined in OpenAPI + + :param code: The code of this ApiResponse. + :param type: The type of this ApiResponse. + :param message: The message of this ApiResponse. + """ + self.openapi_types = { + 'code': int, + 'type': str, + 'message': str + } + + self.attribute_map = { + 'code': 'code', + 'type': 'type', + 'message': 'message' + } + + self._code = code + self._type = type + self._message = message + + @classmethod + def from_dict(cls, dikt: dict) -> 'ApiResponse': + """Returns the dict as a model + + :param dikt: A dict. + :return: The ApiResponse of this ApiResponse. + """ + return util.deserialize_model(dikt, cls) + + @property + def code(self): + """Gets the code of this ApiResponse. + + + :return: The code of this ApiResponse. + :rtype: int + """ + return self._code + + @code.setter + def code(self, code): + """Sets the code of this ApiResponse. + + + :param code: The code of this ApiResponse. + :type code: int + """ + + self._code = code + + @property + def type(self): + """Gets the type of this ApiResponse. + + + :return: The type of this ApiResponse. + :rtype: str + """ + return self._type + + @type.setter + def type(self, type): + """Sets the type of this ApiResponse. + + + :param type: The type of this ApiResponse. + :type type: str + """ + + self._type = type + + @property + def message(self): + """Gets the message of this ApiResponse. + + + :return: The message of this ApiResponse. + :rtype: str + """ + return self._message + + @message.setter + def message(self, message): + """Sets the message of this ApiResponse. + + + :param message: The message of this ApiResponse. + :type message: str + """ + + self._message = message diff --git a/samples/server/petstore/python-aiohttp/openapi_server/models/base_model_.py b/samples/server/petstore/python-aiohttp/openapi_server/models/base_model_.py new file mode 100644 index 000000000000..54f342e56705 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/models/base_model_.py @@ -0,0 +1,66 @@ +import pprint + +import typing + +from openapi_server import util + +T = typing.TypeVar('T') + + +class Model(object): + # openapiTypes: The key is attribute name and the + # value is attribute type. + openapi_types = {} + + # attributeMap: The key is attribute name and the + # value is json key in definition. + attribute_map = {} + + @classmethod + def from_dict(cls: T, dikt: dict) -> T: + """Returns the dict as a model""" + return util.deserialize_model(dikt, cls) + + def to_dict(self) -> dict: + """Returns the model properties as a dict + """ + result = {} + + for attr_key, json_key in self.attribute_map.items(): + value = getattr(self, attr_key) + if value is None: + continue + if isinstance(value, list): + result[json_key] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[json_key] = value.to_dict() + elif isinstance(value, dict): + result[json_key] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[json_key] = value + + return result + + def to_str(self) -> str: + """Returns the string representation of the model + """ + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/samples/server/petstore/python-aiohttp/openapi_server/models/category.py b/samples/server/petstore/python-aiohttp/openapi_server/models/category.py new file mode 100644 index 000000000000..930eef70829a --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/models/category.py @@ -0,0 +1,85 @@ +# coding: utf-8 + +from datetime import date, datetime + +from typing import List, Dict, Type + +from openapi_server.models.base_model_ import Model +from openapi_server import util + + +class Category(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, id: int=None, name: str=None): + """Category - a model defined in OpenAPI + + :param id: The id of this Category. + :param name: The name of this Category. + """ + self.openapi_types = { + 'id': int, + 'name': str + } + + self.attribute_map = { + 'id': 'id', + 'name': 'name' + } + + self._id = id + self._name = name + + @classmethod + def from_dict(cls, dikt: dict) -> 'Category': + """Returns the dict as a model + + :param dikt: A dict. + :return: The Category of this Category. + """ + return util.deserialize_model(dikt, cls) + + @property + def id(self): + """Gets the id of this Category. + + + :return: The id of this Category. + :rtype: int + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this Category. + + + :param id: The id of this Category. + :type id: int + """ + + self._id = id + + @property + def name(self): + """Gets the name of this Category. + + + :return: The name of this Category. + :rtype: str + """ + return self._name + + @name.setter + def name(self, name): + """Sets the name of this Category. + + + :param name: The name of this Category. + :type name: str + """ + + self._name = name diff --git a/samples/server/petstore/python-aiohttp/openapi_server/models/order.py b/samples/server/petstore/python-aiohttp/openapi_server/models/order.py new file mode 100644 index 000000000000..71504aba20cf --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/models/order.py @@ -0,0 +1,193 @@ +# coding: utf-8 + +from datetime import date, datetime + +from typing import List, Dict, Type + +from openapi_server.models.base_model_ import Model +from openapi_server import util + + +class Order(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, id: int=None, pet_id: int=None, quantity: int=None, ship_date: datetime=None, status: str=None, complete: bool=False): + """Order - a model defined in OpenAPI + + :param id: The id of this Order. + :param pet_id: The pet_id of this Order. + :param quantity: The quantity of this Order. + :param ship_date: The ship_date of this Order. + :param status: The status of this Order. + :param complete: The complete of this Order. + """ + self.openapi_types = { + 'id': int, + 'pet_id': int, + 'quantity': int, + 'ship_date': datetime, + 'status': str, + 'complete': bool + } + + self.attribute_map = { + 'id': 'id', + 'pet_id': 'petId', + 'quantity': 'quantity', + 'ship_date': 'shipDate', + 'status': 'status', + 'complete': 'complete' + } + + self._id = id + self._pet_id = pet_id + self._quantity = quantity + self._ship_date = ship_date + self._status = status + self._complete = complete + + @classmethod + def from_dict(cls, dikt: dict) -> 'Order': + """Returns the dict as a model + + :param dikt: A dict. + :return: The Order of this Order. + """ + return util.deserialize_model(dikt, cls) + + @property + def id(self): + """Gets the id of this Order. + + + :return: The id of this Order. + :rtype: int + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this Order. + + + :param id: The id of this Order. + :type id: int + """ + + self._id = id + + @property + def pet_id(self): + """Gets the pet_id of this Order. + + + :return: The pet_id of this Order. + :rtype: int + """ + return self._pet_id + + @pet_id.setter + def pet_id(self, pet_id): + """Sets the pet_id of this Order. + + + :param pet_id: The pet_id of this Order. + :type pet_id: int + """ + + self._pet_id = pet_id + + @property + def quantity(self): + """Gets the quantity of this Order. + + + :return: The quantity of this Order. + :rtype: int + """ + return self._quantity + + @quantity.setter + def quantity(self, quantity): + """Sets the quantity of this Order. + + + :param quantity: The quantity of this Order. + :type quantity: int + """ + + self._quantity = quantity + + @property + def ship_date(self): + """Gets the ship_date of this Order. + + + :return: The ship_date of this Order. + :rtype: datetime + """ + return self._ship_date + + @ship_date.setter + def ship_date(self, ship_date): + """Sets the ship_date of this Order. + + + :param ship_date: The ship_date of this Order. + :type ship_date: datetime + """ + + self._ship_date = ship_date + + @property + def status(self): + """Gets the status of this Order. + + Order Status + + :return: The status of this Order. + :rtype: str + """ + return self._status + + @status.setter + def status(self, status): + """Sets the status of this Order. + + Order Status + + :param status: The status of this Order. + :type status: str + """ + allowed_values = ["placed", "approved", "delivered"] + if status not in allowed_values: + raise ValueError( + "Invalid value for `status` ({0}), must be one of {1}" + .format(status, allowed_values) + ) + + self._status = status + + @property + def complete(self): + """Gets the complete of this Order. + + + :return: The complete of this Order. + :rtype: bool + """ + return self._complete + + @complete.setter + def complete(self, complete): + """Sets the complete of this Order. + + + :param complete: The complete of this Order. + :type complete: bool + """ + + self._complete = complete diff --git a/samples/server/petstore/python-aiohttp/openapi_server/models/pet.py b/samples/server/petstore/python-aiohttp/openapi_server/models/pet.py new file mode 100644 index 000000000000..4931fe6fc972 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/models/pet.py @@ -0,0 +1,199 @@ +# coding: utf-8 + +from datetime import date, datetime + +from typing import List, Dict, Type + +from openapi_server.models.base_model_ import Model +from openapi_server.models.category import Category +from openapi_server.models.tag import Tag +from openapi_server import util + + +class Pet(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, id: int=None, category: Category=None, name: str=None, photo_urls: List[str]=None, tags: List[Tag]=None, status: str=None): + """Pet - a model defined in OpenAPI + + :param id: The id of this Pet. + :param category: The category of this Pet. + :param name: The name of this Pet. + :param photo_urls: The photo_urls of this Pet. + :param tags: The tags of this Pet. + :param status: The status of this Pet. + """ + self.openapi_types = { + 'id': int, + 'category': Category, + 'name': str, + 'photo_urls': List[str], + 'tags': List[Tag], + 'status': str + } + + self.attribute_map = { + 'id': 'id', + 'category': 'category', + 'name': 'name', + 'photo_urls': 'photoUrls', + 'tags': 'tags', + 'status': 'status' + } + + self._id = id + self._category = category + self._name = name + self._photo_urls = photo_urls + self._tags = tags + self._status = status + + @classmethod + def from_dict(cls, dikt: dict) -> 'Pet': + """Returns the dict as a model + + :param dikt: A dict. + :return: The Pet of this Pet. + """ + return util.deserialize_model(dikt, cls) + + @property + def id(self): + """Gets the id of this Pet. + + + :return: The id of this Pet. + :rtype: int + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this Pet. + + + :param id: The id of this Pet. + :type id: int + """ + + self._id = id + + @property + def category(self): + """Gets the category of this Pet. + + + :return: The category of this Pet. + :rtype: Category + """ + return self._category + + @category.setter + def category(self, category): + """Sets the category of this Pet. + + + :param category: The category of this Pet. + :type category: Category + """ + + self._category = category + + @property + def name(self): + """Gets the name of this Pet. + + + :return: The name of this Pet. + :rtype: str + """ + return self._name + + @name.setter + def name(self, name): + """Sets the name of this Pet. + + + :param name: The name of this Pet. + :type name: str + """ + if name is None: + raise ValueError("Invalid value for `name`, must not be `None`") + + self._name = name + + @property + def photo_urls(self): + """Gets the photo_urls of this Pet. + + + :return: The photo_urls of this Pet. + :rtype: List[str] + """ + return self._photo_urls + + @photo_urls.setter + def photo_urls(self, photo_urls): + """Sets the photo_urls of this Pet. + + + :param photo_urls: The photo_urls of this Pet. + :type photo_urls: List[str] + """ + if photo_urls is None: + raise ValueError("Invalid value for `photo_urls`, must not be `None`") + + self._photo_urls = photo_urls + + @property + def tags(self): + """Gets the tags of this Pet. + + + :return: The tags of this Pet. + :rtype: List[Tag] + """ + return self._tags + + @tags.setter + def tags(self, tags): + """Sets the tags of this Pet. + + + :param tags: The tags of this Pet. + :type tags: List[Tag] + """ + + self._tags = tags + + @property + def status(self): + """Gets the status of this Pet. + + pet status in the store + + :return: The status of this Pet. + :rtype: str + """ + return self._status + + @status.setter + def status(self, status): + """Sets the status of this Pet. + + pet status in the store + + :param status: The status of this Pet. + :type status: str + """ + allowed_values = ["available", "pending", "sold"] + if status not in allowed_values: + raise ValueError( + "Invalid value for `status` ({0}), must be one of {1}" + .format(status, allowed_values) + ) + + self._status = status diff --git a/samples/server/petstore/python-aiohttp/openapi_server/models/tag.py b/samples/server/petstore/python-aiohttp/openapi_server/models/tag.py new file mode 100644 index 000000000000..d494277441c7 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/models/tag.py @@ -0,0 +1,85 @@ +# coding: utf-8 + +from datetime import date, datetime + +from typing import List, Dict, Type + +from openapi_server.models.base_model_ import Model +from openapi_server import util + + +class Tag(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, id: int=None, name: str=None): + """Tag - a model defined in OpenAPI + + :param id: The id of this Tag. + :param name: The name of this Tag. + """ + self.openapi_types = { + 'id': int, + 'name': str + } + + self.attribute_map = { + 'id': 'id', + 'name': 'name' + } + + self._id = id + self._name = name + + @classmethod + def from_dict(cls, dikt: dict) -> 'Tag': + """Returns the dict as a model + + :param dikt: A dict. + :return: The Tag of this Tag. + """ + return util.deserialize_model(dikt, cls) + + @property + def id(self): + """Gets the id of this Tag. + + + :return: The id of this Tag. + :rtype: int + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this Tag. + + + :param id: The id of this Tag. + :type id: int + """ + + self._id = id + + @property + def name(self): + """Gets the name of this Tag. + + + :return: The name of this Tag. + :rtype: str + """ + return self._name + + @name.setter + def name(self, name): + """Sets the name of this Tag. + + + :param name: The name of this Tag. + :type name: str + """ + + self._name = name diff --git a/samples/server/petstore/python-aiohttp/openapi_server/models/user.py b/samples/server/petstore/python-aiohttp/openapi_server/models/user.py new file mode 100644 index 000000000000..24e024c54ab5 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/models/user.py @@ -0,0 +1,237 @@ +# coding: utf-8 + +from datetime import date, datetime + +from typing import List, Dict, Type + +from openapi_server.models.base_model_ import Model +from openapi_server import util + + +class User(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, id: int=None, username: str=None, first_name: str=None, last_name: str=None, email: str=None, password: str=None, phone: str=None, user_status: int=None): + """User - a model defined in OpenAPI + + :param id: The id of this User. + :param username: The username of this User. + :param first_name: The first_name of this User. + :param last_name: The last_name of this User. + :param email: The email of this User. + :param password: The password of this User. + :param phone: The phone of this User. + :param user_status: The user_status of this User. + """ + self.openapi_types = { + 'id': int, + 'username': str, + 'first_name': str, + 'last_name': str, + 'email': str, + 'password': str, + 'phone': str, + 'user_status': int + } + + self.attribute_map = { + 'id': 'id', + 'username': 'username', + 'first_name': 'firstName', + 'last_name': 'lastName', + 'email': 'email', + 'password': 'password', + 'phone': 'phone', + 'user_status': 'userStatus' + } + + self._id = id + self._username = username + self._first_name = first_name + self._last_name = last_name + self._email = email + self._password = password + self._phone = phone + self._user_status = user_status + + @classmethod + def from_dict(cls, dikt: dict) -> 'User': + """Returns the dict as a model + + :param dikt: A dict. + :return: The User of this User. + """ + return util.deserialize_model(dikt, cls) + + @property + def id(self): + """Gets the id of this User. + + + :return: The id of this User. + :rtype: int + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this User. + + + :param id: The id of this User. + :type id: int + """ + + self._id = id + + @property + def username(self): + """Gets the username of this User. + + + :return: The username of this User. + :rtype: str + """ + return self._username + + @username.setter + def username(self, username): + """Sets the username of this User. + + + :param username: The username of this User. + :type username: str + """ + + self._username = username + + @property + def first_name(self): + """Gets the first_name of this User. + + + :return: The first_name of this User. + :rtype: str + """ + return self._first_name + + @first_name.setter + def first_name(self, first_name): + """Sets the first_name of this User. + + + :param first_name: The first_name of this User. + :type first_name: str + """ + + self._first_name = first_name + + @property + def last_name(self): + """Gets the last_name of this User. + + + :return: The last_name of this User. + :rtype: str + """ + return self._last_name + + @last_name.setter + def last_name(self, last_name): + """Sets the last_name of this User. + + + :param last_name: The last_name of this User. + :type last_name: str + """ + + self._last_name = last_name + + @property + def email(self): + """Gets the email of this User. + + + :return: The email of this User. + :rtype: str + """ + return self._email + + @email.setter + def email(self, email): + """Sets the email of this User. + + + :param email: The email of this User. + :type email: str + """ + + self._email = email + + @property + def password(self): + """Gets the password of this User. + + + :return: The password of this User. + :rtype: str + """ + return self._password + + @password.setter + def password(self, password): + """Sets the password of this User. + + + :param password: The password of this User. + :type password: str + """ + + self._password = password + + @property + def phone(self): + """Gets the phone of this User. + + + :return: The phone of this User. + :rtype: str + """ + return self._phone + + @phone.setter + def phone(self, phone): + """Sets the phone of this User. + + + :param phone: The phone of this User. + :type phone: str + """ + + self._phone = phone + + @property + def user_status(self): + """Gets the user_status of this User. + + User Status + + :return: The user_status of this User. + :rtype: int + """ + return self._user_status + + @user_status.setter + def user_status(self, user_status): + """Sets the user_status of this User. + + User Status + + :param user_status: The user_status of this User. + :type user_status: int + """ + + self._user_status = user_status diff --git a/samples/server/petstore/python-aiohttp/openapi_server/openapi/openapi.yaml b/samples/server/petstore/python-aiohttp/openapi_server/openapi/openapi.yaml new file mode 100644 index 000000000000..ac5d2ed72afc --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/openapi/openapi.yaml @@ -0,0 +1,792 @@ +openapi: 3.0.1 +info: + description: This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + license: + name: Apache-2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + title: OpenAPI Petstore + version: 1.0.0 +servers: +- url: http://petstore.swagger.io/v2 +tags: +- description: Everything about your Pets + name: pet +- description: Access to Petstore orders + name: store +- description: Operations about user + name: user +paths: + /pet: + post: + operationId: add_pet + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: Pet object that needs to be added to the store + required: true + x-body-name: body + responses: + 405: + content: {} + description: Invalid input + security: + - petstore_auth: + - write:pets + - read:pets + summary: Add a new pet to the store + tags: + - pet + x-codegen-request-body-name: body + x-openapi-router-controller: openapi_server.controllers.pet_controller + put: + operationId: update_pet + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: Pet object that needs to be added to the store + required: true + x-body-name: body + responses: + 400: + content: {} + description: Invalid ID supplied + 404: + content: {} + description: Pet not found + 405: + content: {} + description: Validation exception + security: + - petstore_auth: + - write:pets + - read:pets + summary: Update an existing pet + tags: + - pet + x-codegen-request-body-name: body + x-openapi-router-controller: openapi_server.controllers.pet_controller + /pet/findByStatus: + get: + description: Multiple status values can be provided with comma separated strings + operationId: find_pets_by_status + parameters: + - description: Status values that need to be considered for filter + explode: false + in: query + name: status + required: true + schema: + items: + default: available + enum: + - available + - pending + - sold + type: string + type: array + style: form + responses: + 200: + content: + application/xml: + schema: + items: + $ref: '#/components/schemas/Pet' + type: array + application/json: + schema: + items: + $ref: '#/components/schemas/Pet' + type: array + description: successful operation + 400: + content: {} + description: Invalid status value + security: + - petstore_auth: + - write:pets + - read:pets + summary: Finds Pets by status + tags: + - pet + x-openapi-router-controller: openapi_server.controllers.pet_controller + /pet/findByTags: + get: + deprecated: true + description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + operationId: find_pets_by_tags + parameters: + - description: Tags to filter by + explode: false + in: query + name: tags + required: true + schema: + items: + type: string + type: array + style: form + responses: + 200: + content: + application/xml: + schema: + items: + $ref: '#/components/schemas/Pet' + type: array + application/json: + schema: + items: + $ref: '#/components/schemas/Pet' + type: array + description: successful operation + 400: + content: {} + description: Invalid tag value + security: + - petstore_auth: + - write:pets + - read:pets + summary: Finds Pets by tags + tags: + - pet + x-openapi-router-controller: openapi_server.controllers.pet_controller + /pet/{pet_id}: + delete: + operationId: delete_pet + parameters: + - in: header + name: api_key + schema: + type: string + - description: Pet id to delete + in: path + name: pet_id + required: true + schema: + format: int64 + type: integer + responses: + 400: + content: {} + description: Invalid pet value + security: + - petstore_auth: + - write:pets + - read:pets + summary: Deletes a pet + tags: + - pet + x-openapi-router-controller: openapi_server.controllers.pet_controller + get: + description: Returns a single pet + operationId: get_pet_by_id + parameters: + - description: ID of pet to return + in: path + name: pet_id + required: true + schema: + format: int64 + type: integer + responses: + 200: + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: successful operation + 400: + content: {} + description: Invalid ID supplied + 404: + content: {} + description: Pet not found + security: + - api_key: [] + summary: Find pet by ID + tags: + - pet + x-openapi-router-controller: openapi_server.controllers.pet_controller + post: + operationId: update_pet_with_form + parameters: + - description: ID of pet that needs to be updated + in: path + name: pet_id + required: true + schema: + format: int64 + type: integer + requestBody: + content: + application/x-www-form-urlencoded: + schema: + properties: + name: + description: Updated name of the pet + type: string + status: + description: Updated status of the pet + type: string + x-body-name: body + responses: + 405: + content: {} + description: Invalid input + security: + - petstore_auth: + - write:pets + - read:pets + summary: Updates a pet in the store with form data + tags: + - pet + x-openapi-router-controller: openapi_server.controllers.pet_controller + x-codegen-request-body-name: body + /pet/{pet_id}/uploadImage: + post: + operationId: upload_file + parameters: + - description: ID of pet to update + in: path + name: pet_id + required: true + schema: + format: int64 + type: integer + requestBody: + content: + multipart/form-data: + schema: + properties: + additionalMetadata: + description: Additional data to pass to server + type: string + file: + description: file to upload + format: binary + type: string + x-body-name: body + responses: + 200: + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + description: successful operation + security: + - petstore_auth: + - write:pets + - read:pets + summary: uploads an image + tags: + - pet + x-openapi-router-controller: openapi_server.controllers.pet_controller + x-codegen-request-body-name: body + /store/inventory: + get: + description: Returns a map of status codes to quantities + operationId: get_inventory + responses: + 200: + content: + application/json: + schema: + additionalProperties: + format: int32 + type: integer + type: object + description: successful operation + security: + - api_key: [] + summary: Returns pet inventories by status + tags: + - store + x-openapi-router-controller: openapi_server.controllers.store_controller + /store/order: + post: + operationId: place_order + requestBody: + content: + '*/*': + schema: + $ref: '#/components/schemas/Order' + description: order placed for purchasing the pet + required: true + x-body-name: body + responses: + 200: + content: + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/json: + schema: + $ref: '#/components/schemas/Order' + description: successful operation + 400: + content: {} + description: Invalid Order + summary: Place an order for a pet + tags: + - store + x-codegen-request-body-name: body + x-openapi-router-controller: openapi_server.controllers.store_controller + /store/order/{order_id}: + delete: + description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + operationId: delete_order + parameters: + - description: ID of the order that needs to be deleted + in: path + name: order_id + required: true + schema: + type: string + responses: + 400: + content: {} + description: Invalid ID supplied + 404: + content: {} + description: Order not found + summary: Delete purchase order by ID + tags: + - store + x-openapi-router-controller: openapi_server.controllers.store_controller + get: + description: For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions + operationId: get_order_by_id + parameters: + - description: ID of pet that needs to be fetched + in: path + name: order_id + required: true + schema: + format: int64 + maximum: 5 + minimum: 1 + type: integer + responses: + 200: + content: + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/json: + schema: + $ref: '#/components/schemas/Order' + description: successful operation + 400: + content: {} + description: Invalid ID supplied + 404: + content: {} + description: Order not found + summary: Find purchase order by ID + tags: + - store + x-openapi-router-controller: openapi_server.controllers.store_controller + /user: + post: + description: This can only be done by the logged in user. + operationId: create_user + requestBody: + content: + '*/*': + schema: + $ref: '#/components/schemas/User' + description: Created user object + required: true + x-body-name: body + responses: + default: + content: {} + description: successful operation + summary: Create user + tags: + - user + x-codegen-request-body-name: body + x-openapi-router-controller: openapi_server.controllers.user_controller + /user/createWithArray: + post: + operationId: create_users_with_array_input + requestBody: + content: + '*/*': + schema: + items: + $ref: '#/components/schemas/User' + type: array + description: List of user object + required: true + x-body-name: body + responses: + default: + content: {} + description: successful operation + summary: Creates list of users with given input array + tags: + - user + x-codegen-request-body-name: body + x-openapi-router-controller: openapi_server.controllers.user_controller + /user/createWithList: + post: + operationId: create_users_with_list_input + requestBody: + content: + '*/*': + schema: + items: + $ref: '#/components/schemas/User' + type: array + description: List of user object + required: true + x-body-name: body + responses: + default: + content: {} + description: successful operation + summary: Creates list of users with given input array + tags: + - user + x-codegen-request-body-name: body + x-openapi-router-controller: openapi_server.controllers.user_controller + /user/login: + get: + operationId: login_user + parameters: + - description: The user name for login + in: query + name: username + required: true + schema: + type: string + - description: The password for login in clear text + in: query + name: password + required: true + schema: + type: string + responses: + 200: + content: + application/xml: + schema: + type: string + application/json: + schema: + type: string + description: successful operation + headers: + X-Rate-Limit: + description: calls per hour allowed by the user + schema: + format: int32 + type: integer + X-Expires-After: + description: date in UTC when toekn expires + schema: + format: date-time + type: string + 400: + content: {} + description: Invalid username/password supplied + summary: Logs user into the system + tags: + - user + x-openapi-router-controller: openapi_server.controllers.user_controller + /user/logout: + get: + operationId: logout_user + responses: + default: + content: {} + description: successful operation + summary: Logs out current logged in user session + tags: + - user + x-openapi-router-controller: openapi_server.controllers.user_controller + /user/{username}: + delete: + description: This can only be done by the logged in user. + operationId: delete_user + parameters: + - description: The name that needs to be deleted + in: path + name: username + required: true + schema: + type: string + responses: + 400: + content: {} + description: Invalid username supplied + 404: + content: {} + description: User not found + summary: Delete user + tags: + - user + x-openapi-router-controller: openapi_server.controllers.user_controller + get: + operationId: get_user_by_name + parameters: + - description: The name that needs to be fetched. Use user1 for testing. + in: path + name: username + required: true + schema: + type: string + responses: + 200: + content: + application/xml: + schema: + $ref: '#/components/schemas/User' + application/json: + schema: + $ref: '#/components/schemas/User' + description: successful operation + 400: + content: {} + description: Invalid username supplied + 404: + content: {} + description: User not found + summary: Get user by user name + tags: + - user + x-openapi-router-controller: openapi_server.controllers.user_controller + put: + description: This can only be done by the logged in user. + operationId: update_user + parameters: + - description: name that need to be deleted + in: path + name: username + required: true + schema: + type: string + requestBody: + content: + '*/*': + schema: + $ref: '#/components/schemas/User' + description: Updated user object + required: true + x-body-name: body + responses: + 400: + content: {} + description: Invalid user supplied + 404: + content: {} + description: User not found + summary: Updated user + tags: + - user + x-codegen-request-body-name: body + x-openapi-router-controller: openapi_server.controllers.user_controller +components: + schemas: + Order: + description: An order for a pets from the pet store + example: + petId: 6 + quantity: 1 + id: 0 + shipDate: 2000-01-23T04:56:07.000+00:00 + complete: false + status: placed + properties: + id: + format: int64 + type: integer + petId: + format: int64 + type: integer + quantity: + format: int32 + type: integer + shipDate: + format: date-time + type: string + status: + description: Order Status + enum: + - placed + - approved + - delivered + type: string + complete: + default: false + type: boolean + title: Pet Order + type: object + xml: + name: Order + Category: + description: A category for a pet + example: + name: name + id: 6 + properties: + id: + format: int64 + type: integer + name: + type: string + title: Pet category + type: object + xml: + name: Category + User: + description: A User who is purchasing from the pet store + example: + firstName: firstName + lastName: lastName + password: password + userStatus: 6 + phone: phone + id: 0 + email: email + username: username + properties: + id: + format: int64 + type: integer + username: + type: string + firstName: + type: string + lastName: + type: string + email: + type: string + password: + type: string + phone: + type: string + userStatus: + description: User Status + format: int32 + type: integer + title: a User + type: object + xml: + name: User + Tag: + description: A tag for a pet + example: + name: name + id: 1 + properties: + id: + format: int64 + type: integer + name: + type: string + title: Pet Tag + type: object + xml: + name: Tag + Pet: + description: A pet for sale in the pet store + example: + photoUrls: + - photoUrls + - photoUrls + name: doggie + id: 0 + category: + name: name + id: 6 + tags: + - name: name + id: 1 + - name: name + id: 1 + status: available + properties: + id: + format: int64 + type: integer + category: + $ref: '#/components/schemas/Category' + name: + example: doggie + type: string + photoUrls: + items: + type: string + type: array + xml: + name: photoUrl + wrapped: true + tags: + items: + $ref: '#/components/schemas/Tag' + type: array + xml: + name: tag + wrapped: true + status: + description: pet status in the store + enum: + - available + - pending + - sold + type: string + required: + - name + - photoUrls + title: a Pet + type: object + xml: + name: Pet + ApiResponse: + description: Describes the result of uploading an image resource + example: + code: 0 + type: type + message: message + properties: + code: + format: int32 + type: integer + type: + type: string + message: + type: string + title: An uploaded response + type: object + securitySchemes: + petstore_auth: + flows: + implicit: + authorizationUrl: http://petstore.swagger.io/api/oauth/dialog + scopes: + write:pets: modify pets in your account + read:pets: read your pets + type: oauth2 + x-tokenInfoFunc: openapi_server.controllers.security_controller_.info_from_petstore_auth + x-scopeValidateFunc: openapi_server.controllers.security_controller_.validate_scope_petstore_auth + api_key: + in: header + name: api_key + type: apiKey + x-apikeyInfoFunc: openapi_server.controllers.security_controller_.info_from_api_key diff --git a/samples/server/petstore/python-aiohttp/openapi_server/util.py b/samples/server/petstore/python-aiohttp/openapi_server/util.py new file mode 100644 index 000000000000..9263acb016ea --- /dev/null +++ b/samples/server/petstore/python-aiohttp/openapi_server/util.py @@ -0,0 +1,130 @@ +import datetime + +import typing +from typing import Union + +T = typing.TypeVar('T') +Class = typing.Type[T] + + +def _deserialize(data: Union[dict, list, str], klass: Union[Class, str]) -> Union[dict, list, Class, int, float, str, bool, datetime.date, datetime.datetime]: + """Deserializes dict, list, str into an object. + + :param data: dict, list or str. + :param klass: class literal, or string of class name. + + :return: object. + """ + if data is None: + return None + + if klass in (int, float, str, bool): + return _deserialize_primitive(data, klass) + elif klass == object: + return _deserialize_object(data) + elif klass == datetime.date: + return deserialize_date(data) + elif klass == datetime.datetime: + return deserialize_datetime(data) + elif type(klass) == typing.GenericMeta: + if klass.__extra__ == list: + return _deserialize_list(data, klass.__args__[0]) + if klass.__extra__ == dict: + return _deserialize_dict(data, klass.__args__[1]) + else: + return deserialize_model(data, klass) + + +def _deserialize_primitive(data, klass: Class) -> Union[Class, int, float, str, bool]: + """Deserializes to primitive type. + + :param data: data to deserialize. + :param klass: class literal. + + :return: int, float, str, bool. + """ + try: + value = klass(data) + except (UnicodeEncodeError, TypeError): + value = data + return value + + +def _deserialize_object(value: T) -> T: + """Return an original value. + + :return: object. + """ + return value + + +def deserialize_date(string: str) -> datetime.date: + """Deserializes string to date. + + :param string: str. + :return: date. + """ + try: + from dateutil.parser import parse + return parse(string).date() + except ImportError: + return string + + +def deserialize_datetime(string: str) -> datetime.datetime: + """Deserializes string to datetime. + + The string should be in iso8601 datetime format. + + :param string: str. + :return: datetime. + """ + try: + from dateutil.parser import parse + return parse(string) + except ImportError: + return string + + +def deserialize_model(data: Union[dict, list], klass: T) -> T: + """Deserializes list or dict to model. + + :param data: dict, list. + :param klass: class literal. + :return: model object. + """ + instance = klass() + + if not instance.openapi_types: + return data + + if data is not None and isinstance(data, (list, dict)): + for attr, attr_type in instance.openapi_types.items(): + attr_key = instance.attribute_map[attr] + if attr_key in data: + value = data[attr_key] + setattr(instance, attr, _deserialize(value, attr_type)) + + return instance + + +def _deserialize_list(data: list, boxed_type) -> list: + """Deserializes a list and its elements. + + :param data: list to deserialize. + :param boxed_type: class literal. + + :return: deserialized list. + """ + return [_deserialize(sub_data, boxed_type) for sub_data in data] + + +def _deserialize_dict(data: dict, boxed_type) -> dict: + """Deserializes a dict and its elements. + + :param data: dict to deserialize. + :param boxed_type: class literal. + + :return: deserialized dict. + """ + return {k: _deserialize(v, boxed_type) for k, v in data.items()} diff --git a/samples/server/petstore/python-aiohttp/pom.xml b/samples/server/petstore/python-aiohttp/pom.xml new file mode 100644 index 000000000000..8e77233a458b --- /dev/null +++ b/samples/server/petstore/python-aiohttp/pom.xml @@ -0,0 +1,46 @@ + + 4.0.0 + org.openapitools + PythonAiohttpServer + pom + 1.0-SNAPSHOT + Python Aiohttp Server + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory} + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + pytest-test + integration-test + + exec + + + make + + test-all + + + + + + + + diff --git a/samples/server/petstore/python-aiohttp/requirements.txt b/samples/server/petstore/python-aiohttp/requirements.txt new file mode 100644 index 000000000000..835c75de58a6 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/requirements.txt @@ -0,0 +1,3 @@ +connexion[aiohttp,swagger-ui] == 2.0.2 +swagger-ui-bundle == 0.0.2 +aiohttp_jinja2 == 1.1.0 diff --git a/samples/server/petstore/python-aiohttp/test-requirements.txt b/samples/server/petstore/python-aiohttp/test-requirements.txt new file mode 100644 index 000000000000..1cb425f65519 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/test-requirements.txt @@ -0,0 +1,6 @@ +coverage>=4.0.3 +pytest>=1.3.7 +pluggy>=0.3.1 +py>=1.4.31 +randomize>=0.13 +pytest-aiohttp>=0.3.0 diff --git a/samples/server/petstore/python-aiohttp/test_python3.sh b/samples/server/petstore/python-aiohttp/test_python3.sh new file mode 100755 index 000000000000..c6a92d00aed2 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/test_python3.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +REQUIREMENTS_FILE=requirements.txt +TEST_REQUIREMENTS_FILE=test-requirements.txt +REQUIREMENTS_OUT=requirements.txt.log +SETUP_OUT=*.egg-info +VENV=.venv +DEACTIVE=false + +export LC_ALL=en_US.UTF-8 +export LANG=en_US.UTF-8 + +### set virtualenv +if [ -z "$VIRTUAL_ENV" ]; then + virtualenv $VENV --no-site-packages --always-copy --python python3 + source $VENV/bin/activate + DEACTIVE=true +fi + +### install dependencies +pip install -r $REQUIREMENTS_FILE -r $TEST_REQUIREMENTS_FILE | tee -a $REQUIREMENTS_OUT + +### run tests +pytest || exit 1 + +### static analysis of code +flake8 --show-source petstore_api/ + +### deactivate virtualenv +if [ $DEACTIVE == true ]; then + deactivate +fi diff --git a/samples/server/petstore/python-aiohttp/tests/__init__.py b/samples/server/petstore/python-aiohttp/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/samples/server/petstore/python-aiohttp/tests/conftest.py b/samples/server/petstore/python-aiohttp/tests/conftest.py new file mode 100644 index 000000000000..4eba84b3c113 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/tests/conftest.py @@ -0,0 +1,17 @@ +import logging +import pytest +import os + +import connexion + + +@pytest.fixture +def client(loop, aiohttp_client): + logging.getLogger('connexion.operation').setLevel('ERROR') + options = { + "swagger_ui": True + } + specification_dir = os.path.join(os.path.dirname(__file__), '..', 'openapi_server', 'openapi') + app = connexion.AioHttpApp(__name__, specification_dir=specification_dir, options=options) + app.add_api('openapi.yaml', pass_context_arg_name='request') + return loop.run_until_complete(aiohttp_client(app.app)) diff --git a/samples/server/petstore/python-aiohttp/tests/test_pet_controller.py b/samples/server/petstore/python-aiohttp/tests/test_pet_controller.py new file mode 100644 index 000000000000..5f6384a34f67 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/tests/test_pet_controller.py @@ -0,0 +1,200 @@ +# coding: utf-8 + +import pytest +import json +from aiohttp import web +from aiohttp import FormData + +from openapi_server.models.api_response import ApiResponse +from openapi_server.models.pet import Pet + + +@pytest.mark.skip("Connexion does not support multiple consummes. See https://github.com/zalando/connexion/pull/760") +async def test_add_pet(client): + """Test case for add_pet + + Add a new pet to the store + """ + body = { + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" +} + headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer special-key', + } + response = await client.request( + method='POST', + path='/v2/pet', + headers=headers, + json=body, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +async def test_delete_pet(client): + """Test case for delete_pet + + Deletes a pet + """ + headers = { + 'api_key': 'api_key_example', + 'Authorization': 'Bearer special-key', + } + response = await client.request( + method='DELETE', + path='/v2/pet/{pet_id}'.format(pet_id=56), + headers=headers, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +async def test_find_pets_by_status(client): + """Test case for find_pets_by_status + + Finds Pets by status + """ + params = [('status', 'available')] + headers = { + 'Accept': 'application/json', + 'Authorization': 'Bearer special-key', + } + response = await client.request( + method='GET', + path='/v2/pet/findByStatus', + headers=headers, + params=params, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +async def test_find_pets_by_tags(client): + """Test case for find_pets_by_tags + + Finds Pets by tags + """ + params = [('tags', 'tags_example')] + headers = { + 'Accept': 'application/json', + 'Authorization': 'Bearer special-key', + } + response = await client.request( + method='GET', + path='/v2/pet/findByTags', + headers=headers, + params=params, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +async def test_get_pet_by_id(client): + """Test case for get_pet_by_id + + Find pet by ID + """ + headers = { + 'Accept': 'application/json', + 'api_key': 'special-key', + } + response = await client.request( + method='GET', + path='/v2/pet/{pet_id}'.format(pet_id=56), + headers=headers, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +@pytest.mark.skip("Connexion does not support multiple consummes. See https://github.com/zalando/connexion/pull/760") +async def test_update_pet(client): + """Test case for update_pet + + Update an existing pet + """ + body = { + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" +} + headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer special-key', + } + response = await client.request( + method='PUT', + path='/v2/pet', + headers=headers, + json=body, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +@pytest.mark.skip("application/x-www-form-urlencoded not supported by Connexion") +async def test_update_pet_with_form(client): + """Test case for update_pet_with_form + + Updates a pet in the store with form data + """ + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'Bearer special-key', + } + data = { + 'name': 'name_example', + 'status': 'status_example' + } + response = await client.request( + method='POST', + path='/v2/pet/{pet_id}'.format(pet_id=56), + headers=headers, + data=data, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +@pytest.mark.skip("multipart/form-data not supported by Connexion") +async def test_upload_file(client): + """Test case for upload_file + + uploads an image + """ + headers = { + 'Accept': 'application/json', + 'Content-Type': 'multipart/form-data', + 'Authorization': 'Bearer special-key', + } + data = FormData() + data.add_field('additional_metadata', 'additional_metadata_example') + data.add_field('file', (BytesIO(b'some file data'), 'file.txt')) + response = await client.request( + method='POST', + path='/v2/pet/{pet_id}/uploadImage'.format(pet_id=56), + headers=headers, + data=data, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + diff --git a/samples/server/petstore/python-aiohttp/tests/test_store_controller.py b/samples/server/petstore/python-aiohttp/tests/test_store_controller.py new file mode 100644 index 000000000000..9d376a5f7a5b --- /dev/null +++ b/samples/server/petstore/python-aiohttp/tests/test_store_controller.py @@ -0,0 +1,76 @@ +# coding: utf-8 + +import pytest +import json +from aiohttp import web + +from openapi_server.models.order import Order + + +async def test_delete_order(client): + """Test case for delete_order + + Delete purchase order by ID + """ + headers = { + } + response = await client.request( + method='DELETE', + path='/v2/store/order/{order_id}'.format(order_id='order_id_example'), + headers=headers, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +async def test_get_inventory(client): + """Test case for get_inventory + + Returns pet inventories by status + """ + headers = { + 'Accept': 'application/json', + 'api_key': 'special-key', + } + response = await client.request( + method='GET', + path='/v2/store/inventory', + headers=headers, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +async def test_get_order_by_id(client): + """Test case for get_order_by_id + + Find purchase order by ID + """ + headers = { + 'Accept': 'application/json', + } + response = await client.request( + method='GET', + path='/v2/store/order/{order_id}'.format(order_id=5), + headers=headers, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +@pytest.mark.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") +async def test_place_order(client): + """Test case for place_order + + Place an order for a pet + """ + body = {} + headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + } + response = await client.request( + method='POST', + path='/v2/store/order', + headers=headers, + json=body, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + diff --git a/samples/server/petstore/python-aiohttp/tests/test_user_controller.py b/samples/server/petstore/python-aiohttp/tests/test_user_controller.py new file mode 100644 index 000000000000..307cab0fc7aa --- /dev/null +++ b/samples/server/petstore/python-aiohttp/tests/test_user_controller.py @@ -0,0 +1,149 @@ +# coding: utf-8 + +import pytest +import json +from aiohttp import web + +from openapi_server.models.user import User + + +@pytest.mark.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") +async def test_create_user(client): + """Test case for create_user + + Create user + """ + body = {} + headers = { + 'Content-Type': 'application/json', + } + response = await client.request( + method='POST', + path='/v2/user', + headers=headers, + json=body, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +@pytest.mark.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") +async def test_create_users_with_array_input(client): + """Test case for create_users_with_array_input + + Creates list of users with given input array + """ + body = [] + headers = { + 'Content-Type': 'application/json', + } + response = await client.request( + method='POST', + path='/v2/user/createWithArray', + headers=headers, + json=body, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +@pytest.mark.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") +async def test_create_users_with_list_input(client): + """Test case for create_users_with_list_input + + Creates list of users with given input array + """ + body = [] + headers = { + 'Content-Type': 'application/json', + } + response = await client.request( + method='POST', + path='/v2/user/createWithList', + headers=headers, + json=body, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +async def test_delete_user(client): + """Test case for delete_user + + Delete user + """ + headers = { + } + response = await client.request( + method='DELETE', + path='/v2/user/{username}'.format(username='username_example'), + headers=headers, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +async def test_get_user_by_name(client): + """Test case for get_user_by_name + + Get user by user name + """ + headers = { + 'Accept': 'application/json', + } + response = await client.request( + method='GET', + path='/v2/user/{username}'.format(username='username_example'), + headers=headers, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +async def test_login_user(client): + """Test case for login_user + + Logs user into the system + """ + params = [('username', 'username_example'), + ('password', 'password_example')] + headers = { + 'Accept': 'application/json', + } + response = await client.request( + method='GET', + path='/v2/user/login', + headers=headers, + params=params, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +async def test_logout_user(client): + """Test case for logout_user + + Logs out current logged in user session + """ + headers = { + } + response = await client.request( + method='GET', + path='/v2/user/logout', + headers=headers, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + + +@pytest.mark.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760") +async def test_update_user(client): + """Test case for update_user + + Updated user + """ + body = {} + headers = { + 'Content-Type': 'application/json', + } + response = await client.request( + method='PUT', + path='/v2/user/{username}'.format(username='username_example'), + headers=headers, + json=body, + ) + assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8') + From 005f41c234eeb8987cc3effa19d179eb7195b279 Mon Sep 17 00:00:00 2001 From: Julien Sagnard Date: Wed, 9 Jan 2019 11:18:37 +0100 Subject: [PATCH 3/4] Fix flask tests --- bin/python-aiohttp-petstore.sh | 13 ++- bin/python-flask-petstore-python2.sh | 20 ++++- bin/python-flask-petstore.sh | 19 ++++- .../flaskConnexion/controller_test.mustache | 2 +- .../security_controller_.mustache | 81 +++++++++++++++---- .../security_controller_.mustache | 14 ++-- .../controllers/security_controller_.py | 42 +++++++--- .../test/test_pet_controller.py | 8 +- .../test/test_store_controller.py | 4 +- .../test/test_user_controller.py | 16 ++-- .../controllers/security_controller_.py | 42 +++++++--- .../test/test_pet_controller.py | 8 +- .../test/test_store_controller.py | 4 +- .../test/test_user_controller.py | 16 ++-- .../controllers/security_controller_.py | 6 +- 15 files changed, 212 insertions(+), 83 deletions(-) diff --git a/bin/python-aiohttp-petstore.sh b/bin/python-aiohttp-petstore.sh index 1aec3f2f5172..5c1d32070f42 100755 --- a/bin/python-aiohttp-petstore.sh +++ b/bin/python-aiohttp-petstore.sh @@ -25,8 +25,17 @@ then mvn -B clean package fi +out_folder=samples/server/petstore/python-aiohttp + # if you've executed sbt assembly previously it will use that instead. -export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" -ags="generate -t modules/openapi-generator/src/main/resources/python-aiohttp -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-aiohttp -o samples/server/petstore/python-aiohttp -Dservice $@" +export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties" +ags="generate -t modules/openapi-generator/src/main/resources/python-aiohttp -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-aiohttp -o $out_folder -Dservice $@" + +rm -rf $out_folder/.openapi* +rm -rf $out_folder/openapi_server +rm -rf $out_folder/tests* +rm $out_folder/README.md +rm $out_folder/requirements.txt +rm $out_folder/test-requirements.txt java $JAVA_OPTS -jar $executable $ags diff --git a/bin/python-flask-petstore-python2.sh b/bin/python-flask-petstore-python2.sh index 7b8ec1a68e71..5ba265e4cabb 100755 --- a/bin/python-flask-petstore-python2.sh +++ b/bin/python-flask-petstore-python2.sh @@ -25,9 +25,23 @@ then mvn -B clean package fi +out_folder=samples/server/petstore/flaskConnexion-python2 + # if you've executed sbt assembly previously it will use that instead. -export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" -#ags="generate -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion-python2 -DsupportPython2=true $@" -ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion-python2 -c bin/supportPython2.json -D service $@" +export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties" +ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o $out_folder -c bin/supportPython2.json -D service $@" + +rm -rf $out_folder/.openapi* +rm -rf $out_folder/openapi_server +rm $out_folder/.dockerignore +rm $out_folder/.gitignore +rm $out_folder/.travis.yml +rm $out_folder/Dockerfile +rm $out_folder/git_push.sh +rm $out_folder/README.md +rm $out_folder/requirements.txt +rm $out_folder/setup.py +rm $out_folder/test-requirements.txt +rm $out_folder/tox.ini java $JAVA_OPTS -jar $executable $ags diff --git a/bin/python-flask-petstore.sh b/bin/python-flask-petstore.sh index 0126e5e4624d..670c1fbdad48 100755 --- a/bin/python-flask-petstore.sh +++ b/bin/python-flask-petstore.sh @@ -25,8 +25,23 @@ then mvn -B clean package fi +out_folder=samples/server/petstore/flaskConnexion + # if you've executed sbt assembly previously it will use that instead. -export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" -ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion -Dservice $@" +export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties" +ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o $out_folder -Dservice $@" + +rm -rf $out_folder/.openapi* +rm -rf $out_folder/openapi_server +rm $out_folder/.dockerignore +rm $out_folder/.gitignore +rm $out_folder/.travis.yml +rm $out_folder/Dockerfile +rm $out_folder/git_push.sh +rm $out_folder/README.md +rm $out_folder/requirements.txt +rm $out_folder/setup.py +rm $out_folder/test-requirements.txt +rm $out_folder/tox.ini java $JAVA_OPTS -jar $executable $ags diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/controller_test.mustache b/modules/openapi-generator/src/main/resources/flaskConnexion/controller_test.mustache index 44f6d1ea19f4..665728619282 100644 --- a/modules/openapi-generator/src/main/resources/flaskConnexion/controller_test.mustache +++ b/modules/openapi-generator/src/main/resources/flaskConnexion/controller_test.mustache @@ -33,7 +33,7 @@ class {{#operations}}Test{{classname}}(BaseTestCase): 'Accept': '{{mediaType}}',{{/vendorExtensions.x-prefered-produce}}{{#vendorExtensions.x-prefered-consume}} 'Content-Type': '{{mediaType}}',{{/vendorExtensions.x-prefered-consume}}{{#headerParams}} '{{paramName}}': {{{example}}},{{/headerParams}}{{#authMethods}} - {{#isOAuth}}'Authorization': 'Bearer special-key',{{/isOAuth}}{{#isApiKey}}'{{name}}': 'special-key',{{/isApiKey}}{{#isBasic}}'Authorization': '{{#isBasicBasic}}{{#isBasicBasic}} BasicZm9vOmJhcg=={{#isBasicBearer}}Bearer special-key{{#isBasicBearer}}',{{/isBasic}}{{/authMethods}} + {{#isOAuth}}'Authorization': 'Bearer special-key',{{/isOAuth}}{{#isApiKey}}'{{name}}': 'special-key',{{/isApiKey}}{{#isBasicBasic}}'Authorization': 'BasicZm9vOmJhcg==',{{/isBasicBasic}}{{#isBasicBearer}}'Authorization': 'Bearer special-key',{{/isBasicBearer}}{{/authMethods}} } {{#formParams}} {{#-first}}data = dict({{/-first}}{{^-first}} {{/-first}}{{paramName}}={{{example}}}{{#hasMore}},{{/hasMore}}{{#-last}}){{/-last}} diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/security_controller_.mustache b/modules/openapi-generator/src/main/resources/flaskConnexion/security_controller_.mustache index 10f248927979..f6293adda19e 100644 --- a/modules/openapi-generator/src/main/resources/flaskConnexion/security_controller_.mustache +++ b/modules/openapi-generator/src/main/resources/flaskConnexion/security_controller_.mustache @@ -1,43 +1,90 @@ +from typing import List + {{#authMethods}} {{#isOAuth}} -def info_from_token_{{name}}(token): +def info_from_{{name}}(token): """ Validate and decode token. - Returned value will be available in request['token_info']. - 'sub' or 'uid' will be set in request['user']. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. 'scope' or 'scopes' will be passed to scope validation function. - Should return None if token is invalid + + :param token Token provided by Authorization header + :type token: str + :return: Decoded token information or None if token is invalid + :rtype: dict | None """ return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} def validate_scope_{{name}}(required_scopes, token_scopes): - """ Validate required scopes are included in token scope """ + """ + Validate required scopes are included in token scope + + :param required_scopes Required scope to access called API + :type required_scopes: List[str] + :param token_scopes Scope present in token + :type token_scopes: List[str] + :return: True if access to called API is allowed + :rtype: bool + """ return set(required_scopes).issubset(set(token_scopes)) {{/isOAuth}} {{#isApiKey}} -def info_from_api_key_{{name}}(api_key, required_scopes): +def info_from_{{name}}(api_key, required_scopes): """ - Returned value will be available in request['token_info']. - 'sub' or 'uid' will be set in request['user']. - Should return None if api_key is invalid + Check and retrieve authentication information from api_key. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. + + :param api_key API key provided by Authorization header + :type api_key: str + :param required_scopes Always None. Used for other authentication method + :type required_scopes: None + :return: Information attached to provided api_key or None if api_key is invalid or does not allow access to called API + :rtype: dict | None """ - return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + return {'uid': 'user_id'} {{/isApiKey}} -{{#isBasic}} +{{#isBasicBasic}} -def info_from_basic_auth_{{name}}(username, password, required_scopes): +def info_from_{{name}}(username, password, required_scopes): """ - Returned value will be available in request['token_info']. - 'sub' or 'uid' will be set in request['user']. - Should return None if auth is invalid + Check and retrieve authentication information from basic auth. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. + + :param username login provided by Authorization header + :type username: str + :param password password provided by Authorization header + :type password: str + :param required_scopes Always None. Used for other authentication method + :type required_scopes: None + :return: Information attached to user or None if credentials are invalid or does not allow access to called API + :rtype: dict | None + """ + return {'uid': 'user_id'} + +{{/isBasicBasic}} +{{#isBasicBearer}} + +def info_from_{{name}}(token): + """ + Check and retrieve authentication information from custom bearer token. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. + + :param token Token provided by Authorization header + :type token: str + :return: Decoded token information or None if token is invalid + :rtype: dict | None """ - return {'scopes': ['read:pets', 'write:pets'], 'uid': username} + return {'uid': 'user_id'} -{{/isBasic}} +{{/isBasicBearer}} {{/authMethods}} diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/security_controller_.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/security_controller_.mustache index 04aeecc10460..bf7bf2b36d69 100644 --- a/modules/openapi-generator/src/main/resources/python-aiohttp/security_controller_.mustache +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/security_controller_.mustache @@ -9,7 +9,7 @@ def info_from_{{name}}(token: str) -> dict: Returned value will be passed in 'token_info' parameter of your operation function, if there is one. 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. 'scope' or 'scopes' will be passed to scope validation function. - Should return None if token is invalid + Should return None if token is invalid or does not allow access to called API. """ return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} @@ -26,9 +26,9 @@ def info_from_{{name}}(api_key: str, required_scopes: None) -> dict: Check and retrieve authentication information from api_key. Returned value will be passed in 'token_info' parameter of your operation function, if there is one. 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. - Should return None if api_key is invalid + Should return None if api_key is invalid or does not allow access to called API. """ - return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + return {'uid': 'user_id'} {{/isApiKey}} {{#isBasicBasic}} @@ -38,9 +38,9 @@ def info_from_{{name}}(username: str, password: str, required_scopes: None) -> d Check and retrieve authentication information from basic auth. Returned value will be passed in 'token_info' parameter of your operation function, if there is one. 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. - Should return None if auth is invalid + Should return None if auth is invalid or does not allow access to called API. """ - return {'scopes': ['read:pets', 'write:pets'], 'uid': username} + return {'uid': 'user_id'} {{/isBasicBasic}} {{#isBasicBearer}} @@ -50,9 +50,9 @@ def info_from_{{name}}(token: str) -> dict: Check and retrieve authentication information from custom bearer token. Returned value will be passed in 'token_info' parameter of your operation function, if there is one. 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. - Should return None if auth is invalid + Should return None if auth is invalid or does not allow access to called API. """ - return {'scopes': ['read:pets', 'write:pets'], 'uid': 'username'} + return {'uid': 'user_id'} {{/isBasicBearer}} {{/authMethods}} diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/security_controller_.py b/samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/security_controller_.py index cbf0d187d4bc..1db7a68f4665 100644 --- a/samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/security_controller_.py +++ b/samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/security_controller_.py @@ -1,26 +1,48 @@ +from typing import List -def info_from_api_key_api_key(api_key, required_scopes): + +def info_from_api_key(api_key, required_scopes): """ - Returned value will be available in request['token_info']. - 'sub' or 'uid' will be set in request['user']. - Should return None if api_key is invalid + Check and retrieve authentication information from api_key. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. + + :param api_key API key provided by Authorization header + :type api_key: str + :param required_scopes Always None. Used for other authentication method + :type required_scopes: None + :return: Information attached to provided api_key or None if api_key is invalid or does not allow access to called API + :rtype: dict | None """ - return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + return {'uid': 'user_id'} -def info_from_token_petstore_auth(token): +def info_from_petstore_auth(token): """ Validate and decode token. - Returned value will be available in request['token_info']. - 'sub' or 'uid' will be set in request['user']. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. 'scope' or 'scopes' will be passed to scope validation function. - Should return None if token is invalid + + :param token Token provided by Authorization header + :type token: str + :return: Decoded token information or None if token is invalid + :rtype: dict | None """ return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} def validate_scope_petstore_auth(required_scopes, token_scopes): - """ Validate required scopes are included in token scope """ + """ + Validate required scopes are included in token scope + + :param required_scopes Required scope to access called API + :type required_scopes: List[str] + :param token_scopes Scope present in token + :type token_scopes: List[str] + :return: True if access to called API is allowed + :rtype: bool + """ return set(required_scopes).issubset(set(token_scopes)) diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_pet_controller.py b/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_pet_controller.py index 230ac4a4b607..3100b498b35f 100644 --- a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_pet_controller.py +++ b/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_pet_controller.py @@ -20,7 +20,7 @@ def test_add_pet(self): Add a new pet to the store """ - pet = { + body = { "photoUrls" : [ "photoUrls", "photoUrls" ], "name" : "doggie", "id" : 0, @@ -45,7 +45,7 @@ def test_add_pet(self): '/v2/pet', method='POST', headers=headers, - data=json.dumps(pet), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -124,7 +124,7 @@ def test_update_pet(self): Update an existing pet """ - pet = { + body = { "photoUrls" : [ "photoUrls", "photoUrls" ], "name" : "doggie", "id" : 0, @@ -149,7 +149,7 @@ def test_update_pet(self): '/v2/pet', method='PUT', headers=headers, - data=json.dumps(pet), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_store_controller.py b/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_store_controller.py index ea8c95b20ef7..e2d0c7ddd372 100644 --- a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_store_controller.py +++ b/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_store_controller.py @@ -64,7 +64,7 @@ def test_place_order(self): Place an order for a pet """ - order = {} + body = {} headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', @@ -73,7 +73,7 @@ def test_place_order(self): '/v2/store/order', method='POST', headers=headers, - data=json.dumps(order), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_user_controller.py b/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_user_controller.py index d1d970eec6dc..78cce39287ec 100644 --- a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_user_controller.py +++ b/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_user_controller.py @@ -19,7 +19,7 @@ def test_create_user(self): Create user """ - user = {} + body = {} headers = { 'Content-Type': 'application/json', } @@ -27,7 +27,7 @@ def test_create_user(self): '/v2/user', method='POST', headers=headers, - data=json.dumps(user), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -38,7 +38,7 @@ def test_create_users_with_array_input(self): Creates list of users with given input array """ - user = [] + body = [] headers = { 'Content-Type': 'application/json', } @@ -46,7 +46,7 @@ def test_create_users_with_array_input(self): '/v2/user/createWithArray', method='POST', headers=headers, - data=json.dumps(user), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -57,7 +57,7 @@ def test_create_users_with_list_input(self): Creates list of users with given input array """ - user = [] + body = [] headers = { 'Content-Type': 'application/json', } @@ -65,7 +65,7 @@ def test_create_users_with_list_input(self): '/v2/user/createWithList', method='POST', headers=headers, - data=json.dumps(user), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -137,7 +137,7 @@ def test_update_user(self): Updated user """ - user = {} + body = {} headers = { 'Content-Type': 'application/json', } @@ -145,7 +145,7 @@ def test_update_user(self): '/v2/user/{username}'.format(username='username_example'), method='PUT', headers=headers, - data=json.dumps(user), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) diff --git a/samples/server/petstore/flaskConnexion/openapi_server/controllers/security_controller_.py b/samples/server/petstore/flaskConnexion/openapi_server/controllers/security_controller_.py index cbf0d187d4bc..1db7a68f4665 100644 --- a/samples/server/petstore/flaskConnexion/openapi_server/controllers/security_controller_.py +++ b/samples/server/petstore/flaskConnexion/openapi_server/controllers/security_controller_.py @@ -1,26 +1,48 @@ +from typing import List -def info_from_api_key_api_key(api_key, required_scopes): + +def info_from_api_key(api_key, required_scopes): """ - Returned value will be available in request['token_info']. - 'sub' or 'uid' will be set in request['user']. - Should return None if api_key is invalid + Check and retrieve authentication information from api_key. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. + + :param api_key API key provided by Authorization header + :type api_key: str + :param required_scopes Always None. Used for other authentication method + :type required_scopes: None + :return: Information attached to provided api_key or None if api_key is invalid or does not allow access to called API + :rtype: dict | None """ - return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + return {'uid': 'user_id'} -def info_from_token_petstore_auth(token): +def info_from_petstore_auth(token): """ Validate and decode token. - Returned value will be available in request['token_info']. - 'sub' or 'uid' will be set in request['user']. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. 'scope' or 'scopes' will be passed to scope validation function. - Should return None if token is invalid + + :param token Token provided by Authorization header + :type token: str + :return: Decoded token information or None if token is invalid + :rtype: dict | None """ return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} def validate_scope_petstore_auth(required_scopes, token_scopes): - """ Validate required scopes are included in token scope """ + """ + Validate required scopes are included in token scope + + :param required_scopes Required scope to access called API + :type required_scopes: List[str] + :param token_scopes Scope present in token + :type token_scopes: List[str] + :return: True if access to called API is allowed + :rtype: bool + """ return set(required_scopes).issubset(set(token_scopes)) diff --git a/samples/server/petstore/flaskConnexion/openapi_server/test/test_pet_controller.py b/samples/server/petstore/flaskConnexion/openapi_server/test/test_pet_controller.py index c756d7bb2ca1..f29ac97214ba 100644 --- a/samples/server/petstore/flaskConnexion/openapi_server/test/test_pet_controller.py +++ b/samples/server/petstore/flaskConnexion/openapi_server/test/test_pet_controller.py @@ -20,7 +20,7 @@ def test_add_pet(self): Add a new pet to the store """ - pet = { + body = { "photoUrls" : [ "photoUrls", "photoUrls" ], "name" : "doggie", "id" : 0, @@ -45,7 +45,7 @@ def test_add_pet(self): '/v2/pet', method='POST', headers=headers, - data=json.dumps(pet), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -124,7 +124,7 @@ def test_update_pet(self): Update an existing pet """ - pet = { + body = { "photoUrls" : [ "photoUrls", "photoUrls" ], "name" : "doggie", "id" : 0, @@ -149,7 +149,7 @@ def test_update_pet(self): '/v2/pet', method='PUT', headers=headers, - data=json.dumps(pet), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) diff --git a/samples/server/petstore/flaskConnexion/openapi_server/test/test_store_controller.py b/samples/server/petstore/flaskConnexion/openapi_server/test/test_store_controller.py index ea8c95b20ef7..e2d0c7ddd372 100644 --- a/samples/server/petstore/flaskConnexion/openapi_server/test/test_store_controller.py +++ b/samples/server/petstore/flaskConnexion/openapi_server/test/test_store_controller.py @@ -64,7 +64,7 @@ def test_place_order(self): Place an order for a pet """ - order = {} + body = {} headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', @@ -73,7 +73,7 @@ def test_place_order(self): '/v2/store/order', method='POST', headers=headers, - data=json.dumps(order), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) diff --git a/samples/server/petstore/flaskConnexion/openapi_server/test/test_user_controller.py b/samples/server/petstore/flaskConnexion/openapi_server/test/test_user_controller.py index d1d970eec6dc..78cce39287ec 100644 --- a/samples/server/petstore/flaskConnexion/openapi_server/test/test_user_controller.py +++ b/samples/server/petstore/flaskConnexion/openapi_server/test/test_user_controller.py @@ -19,7 +19,7 @@ def test_create_user(self): Create user """ - user = {} + body = {} headers = { 'Content-Type': 'application/json', } @@ -27,7 +27,7 @@ def test_create_user(self): '/v2/user', method='POST', headers=headers, - data=json.dumps(user), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -38,7 +38,7 @@ def test_create_users_with_array_input(self): Creates list of users with given input array """ - user = [] + body = [] headers = { 'Content-Type': 'application/json', } @@ -46,7 +46,7 @@ def test_create_users_with_array_input(self): '/v2/user/createWithArray', method='POST', headers=headers, - data=json.dumps(user), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -57,7 +57,7 @@ def test_create_users_with_list_input(self): Creates list of users with given input array """ - user = [] + body = [] headers = { 'Content-Type': 'application/json', } @@ -65,7 +65,7 @@ def test_create_users_with_list_input(self): '/v2/user/createWithList', method='POST', headers=headers, - data=json.dumps(user), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -137,7 +137,7 @@ def test_update_user(self): Updated user """ - user = {} + body = {} headers = { 'Content-Type': 'application/json', } @@ -145,7 +145,7 @@ def test_update_user(self): '/v2/user/{username}'.format(username='username_example'), method='PUT', headers=headers, - data=json.dumps(user), + data=json.dumps(body), content_type='application/json') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) diff --git a/samples/server/petstore/python-aiohttp/openapi_server/controllers/security_controller_.py b/samples/server/petstore/python-aiohttp/openapi_server/controllers/security_controller_.py index 5815dd85d4a3..90ce5c351a1e 100644 --- a/samples/server/petstore/python-aiohttp/openapi_server/controllers/security_controller_.py +++ b/samples/server/petstore/python-aiohttp/openapi_server/controllers/security_controller_.py @@ -6,9 +6,9 @@ def info_from_api_key(api_key: str, required_scopes: None) -> dict: Check and retrieve authentication information from api_key. Returned value will be passed in 'token_info' parameter of your operation function, if there is one. 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. - Should return None if api_key is invalid + Should return None if api_key is invalid or does not allow access to called API. """ - return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} + return {'uid': 'user_id'} def info_from_petstore_auth(token: str) -> dict: @@ -17,7 +17,7 @@ def info_from_petstore_auth(token: str) -> dict: Returned value will be passed in 'token_info' parameter of your operation function, if there is one. 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. 'scope' or 'scopes' will be passed to scope validation function. - Should return None if token is invalid + Should return None if token is invalid or does not allow access to called API. """ return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} From 6549dc8bb69599c1c8cd0070836929543fea487c Mon Sep 17 00:00:00 2001 From: Julien Sagnard Date: Wed, 9 Jan 2019 11:55:34 +0100 Subject: [PATCH 4/4] Normalize python-flask folder names --- bin/openapi3/python-flask-petstore-python2.sh | 24 +++++++++++++++---- bin/openapi3/python-flask-petstore.sh | 23 +++++++++++++++--- bin/python-flask-all.sh | 4 ---- ...e.sh => python-server-aiohttp-petstore.sh} | 7 ++++-- bin/python-server-all.sh | 5 ++++ ...> python-server-flask-petstore-python2.sh} | 7 ++++-- ...on2.sh => python-server-flask-petstore.sh} | 7 ++++-- bin/windows/python2-flask-petstore.bat | 2 +- bin/windows/python3-flask-petstore.bat | 2 +- .../PythonFlaskConnexionServerCodegen.java | 2 +- .../Dockerfile.mustache | 0 .../README.mustache | 0 .../__init__.mustache | 0 .../__init__model.mustache | 0 .../__init__test.mustache | 0 .../__main__.mustache | 0 .../base_model_.mustache | 0 .../controller.mustache | 0 .../controller_test.mustache | 0 .../dockerignore.mustache | 0 .../encoder.mustache | 0 .../git_push.sh.mustache | 0 .../gitignore.mustache | 0 .../model.mustache | 0 .../openapi.mustache | 0 .../param_type.mustache | 0 .../requirements.mustache | 0 .../security_controller_.mustache | 0 .../setup.mustache | 0 .../test-requirements.mustache | 0 .../tox.mustache | 0 .../travis.mustache | 0 .../util.mustache | 0 pom.xml | 4 ++-- .../.dockerignore | 0 .../.gitignore | 0 .../.openapi-generator-ignore | 0 .../.openapi-generator/VERSION | 0 .../.travis.yml | 0 .../Dockerfile | 0 .../Makefile | 0 .../README.md | 0 .../git_push.sh | 0 .../openapi_server/__init__.py | 0 .../openapi_server/__main__.py | 0 .../openapi_server/controllers/__init__.py | 0 .../controllers/pet_controller.py | 0 .../controllers/security_controller_.py | 0 .../controllers/store_controller.py | 0 .../controllers/user_controller.py | 0 .../openapi_server/encoder.py | 0 .../openapi_server/models/__init__.py | 0 .../openapi_server/models/api_response.py | 0 .../openapi_server/models/base_model_.py | 0 .../openapi_server/models/category.py | 0 .../openapi_server/models/order.py | 0 .../openapi_server/models/pet.py | 0 .../openapi_server/models/tag.py | 0 .../openapi_server/models/user.py | 0 .../openapi_server/openapi/openapi.yaml | 0 .../openapi_server/test/__init__.py | 0 .../test/test_pet_controller.py | 0 .../test/test_store_controller.py | 0 .../test/test_user_controller.py | 0 .../openapi_server/util.py | 0 .../pom.xml | 2 +- .../requirements.txt | 0 .../setup.py | 0 .../test-requirements.txt | 0 .../test_python2.sh | 0 .../tox.ini | 0 .../.dockerignore | 0 .../.gitignore | 0 .../.openapi-generator-ignore | 0 .../.openapi-generator/VERSION | 0 .../.travis.yml | 0 .../Dockerfile | 0 .../{flaskConnexion => python-flask}/Makefile | 0 .../README.md | 0 .../git_push.sh | 0 .../openapi_server/__init__.py | 0 .../openapi_server/__main__.py | 0 .../openapi_server/controllers/__init__.py | 0 .../controllers/pet_controller.py | 0 .../controllers/security_controller_.py | 0 .../controllers/store_controller.py | 0 .../controllers/user_controller.py | 0 .../openapi_server/encoder.py | 0 .../openapi_server/models/__init__.py | 0 .../openapi_server/models/api_response.py | 0 .../openapi_server/models/base_model_.py | 0 .../openapi_server/models/category.py | 0 .../openapi_server/models/order.py | 0 .../openapi_server/models/pet.py | 0 .../openapi_server/models/tag.py | 0 .../openapi_server/models/user.py | 0 .../openapi_server/openapi/openapi.yaml | 0 .../openapi_server/test/__init__.py | 0 .../test/test_pet_controller.py | 0 .../test/test_store_controller.py | 0 .../test/test_user_controller.py | 0 .../openapi_server/util.py | 0 .../{flaskConnexion => python-flask}/pom.xml | 2 +- .../requirements.txt | 0 .../{flaskConnexion => python-flask}/setup.py | 0 .../test-requirements.txt | 0 .../test_python3.sh | 0 .../{flaskConnexion => python-flask}/tox.ini | 0 108 files changed, 67 insertions(+), 24 deletions(-) delete mode 100755 bin/python-flask-all.sh rename bin/{python-aiohttp-petstore.sh => python-server-aiohttp-petstore.sh} (75%) create mode 100755 bin/python-server-all.sh rename bin/{python-flask-petstore.sh => python-server-flask-petstore-python2.sh} (76%) rename bin/{python-flask-petstore-python2.sh => python-server-flask-petstore.sh} (78%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/Dockerfile.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/README.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/__init__.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/__init__model.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/__init__test.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/__main__.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/base_model_.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/controller.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/controller_test.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/dockerignore.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/encoder.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/git_push.sh.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/gitignore.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/model.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/openapi.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/param_type.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/requirements.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/security_controller_.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/setup.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/test-requirements.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/tox.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/travis.mustache (100%) rename modules/openapi-generator/src/main/resources/{flaskConnexion => python-flask}/util.mustache (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/.dockerignore (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/.gitignore (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/.openapi-generator-ignore (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/.openapi-generator/VERSION (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/.travis.yml (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/Dockerfile (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/Makefile (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/README.md (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/git_push.sh (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/__init__.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/__main__.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/controllers/__init__.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/controllers/pet_controller.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/controllers/security_controller_.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/controllers/store_controller.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/controllers/user_controller.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/encoder.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/models/__init__.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/models/api_response.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/models/base_model_.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/models/category.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/models/order.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/models/pet.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/models/tag.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/models/user.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/openapi/openapi.yaml (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/test/__init__.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/test/test_pet_controller.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/test/test_store_controller.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/test/test_user_controller.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/openapi_server/util.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/pom.xml (96%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/requirements.txt (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/setup.py (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/test-requirements.txt (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/test_python2.sh (100%) rename samples/server/petstore/{flaskConnexion-python2 => python-flask-python2}/tox.ini (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/.dockerignore (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/.gitignore (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/.openapi-generator-ignore (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/.openapi-generator/VERSION (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/.travis.yml (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/Dockerfile (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/Makefile (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/README.md (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/git_push.sh (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/__init__.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/__main__.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/controllers/__init__.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/controllers/pet_controller.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/controllers/security_controller_.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/controllers/store_controller.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/controllers/user_controller.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/encoder.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/models/__init__.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/models/api_response.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/models/base_model_.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/models/category.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/models/order.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/models/pet.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/models/tag.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/models/user.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/openapi/openapi.yaml (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/test/__init__.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/test/test_pet_controller.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/test/test_store_controller.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/test/test_user_controller.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/openapi_server/util.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/pom.xml (96%) rename samples/server/petstore/{flaskConnexion => python-flask}/requirements.txt (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/setup.py (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/test-requirements.txt (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/test_python3.sh (100%) rename samples/server/petstore/{flaskConnexion => python-flask}/tox.ini (100%) diff --git a/bin/openapi3/python-flask-petstore-python2.sh b/bin/openapi3/python-flask-petstore-python2.sh index f804f1a69dc0..c98569d10c4d 100755 --- a/bin/openapi3/python-flask-petstore-python2.sh +++ b/bin/openapi3/python-flask-petstore-python2.sh @@ -26,9 +26,25 @@ then fi # if you've executed sbt assembly previously it will use that instead. -export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" -#ags="generate -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion-python2 -DsupportPython2=true $@" -ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion-python2 -c bin/supportPython2.json -D service $@" +input=modules/openapi-generator/src/test/resources/3_0/petstore.yaml +out_folder=samples/server/openapi3/petstore/python-flask-python2 +resources=modules/openapi-generator/src/main/resources/python-flask + +# if you've executed sbt assembly previously it will use that instead. +export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties" +ags="generate -t $resources -i $input -g python-flask -o $out_folder -c bin/supportPython2.json -D service $@" + +rm -rf $out_folder/.openapi* +rm -rf $out_folder/openapi_server +rm $out_folder/.dockerignore +rm $out_folder/.gitignore +rm $out_folder/.travis.yml +rm $out_folder/Dockerfile +rm $out_folder/git_push.sh +rm $out_folder/README.md +rm $out_folder/requirements.txt +rm $out_folder/setup.py +rm $out_folder/test-requirements.txt +rm $out_folder/tox.ini -rm -rf samples/server/petstore/flaskConnexion-python2/* java $JAVA_OPTS -jar $executable $ags diff --git a/bin/openapi3/python-flask-petstore.sh b/bin/openapi3/python-flask-petstore.sh index 8fce1e0c8cf6..473c69a7b2c6 100755 --- a/bin/openapi3/python-flask-petstore.sh +++ b/bin/openapi3/python-flask-petstore.sh @@ -26,8 +26,25 @@ then fi # if you've executed sbt assembly previously it will use that instead. -export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" -ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion -Dservice $@" +input=modules/openapi-generator/src/test/resources/3_0/petstore.yaml +out_folder=samples/server/openapi3/petstore/python-flask +resources=modules/openapi-generator/src/main/resources/python-flask + +# if you've executed sbt assembly previously it will use that instead. +export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties" +ags="generate -t $resources -i $input -g python-flask -o $out_folder -Dservice $@" + +rm -rf $out_folder/.openapi* +rm -rf $out_folder/openapi_server +rm $out_folder/.dockerignore +rm $out_folder/.gitignore +rm $out_folder/.travis.yml +rm $out_folder/Dockerfile +rm $out_folder/git_push.sh +rm $out_folder/README.md +rm $out_folder/requirements.txt +rm $out_folder/setup.py +rm $out_folder/test-requirements.txt +rm $out_folder/tox.ini -rm -rf samples/server/petstore/flaskConnexion/* java $JAVA_OPTS -jar $executable $ags diff --git a/bin/python-flask-all.sh b/bin/python-flask-all.sh deleted file mode 100755 index ed4ca88d286f..000000000000 --- a/bin/python-flask-all.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -./bin/python-flask-petstore.sh -./bin/python-flask-petstore-python2.sh diff --git a/bin/python-aiohttp-petstore.sh b/bin/python-server-aiohttp-petstore.sh similarity index 75% rename from bin/python-aiohttp-petstore.sh rename to bin/python-server-aiohttp-petstore.sh index 5c1d32070f42..38aae23d5f3c 100755 --- a/bin/python-aiohttp-petstore.sh +++ b/bin/python-server-aiohttp-petstore.sh @@ -25,11 +25,14 @@ then mvn -B clean package fi -out_folder=samples/server/petstore/python-aiohttp +generator=python-aiohttp +input=modules/openapi-generator/src/test/resources/2_0/petstore.yaml +out_folder=samples/server/petstore/$generator +resources=modules/openapi-generator/src/main/resources/$generator # if you've executed sbt assembly previously it will use that instead. export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties" -ags="generate -t modules/openapi-generator/src/main/resources/python-aiohttp -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-aiohttp -o $out_folder -Dservice $@" +ags="generate -t $resources -i $input -g $generator -o $out_folder -Dservice $@" rm -rf $out_folder/.openapi* rm -rf $out_folder/openapi_server diff --git a/bin/python-server-all.sh b/bin/python-server-all.sh new file mode 100755 index 000000000000..5a4f5c754891 --- /dev/null +++ b/bin/python-server-all.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +./bin/python-server-aiohttp-petstore.sh +./bin/python-server-flask-petstore.sh +./bin/python-server-flask-petstore-python2.sh diff --git a/bin/python-flask-petstore.sh b/bin/python-server-flask-petstore-python2.sh similarity index 76% rename from bin/python-flask-petstore.sh rename to bin/python-server-flask-petstore-python2.sh index 670c1fbdad48..debbb458a153 100755 --- a/bin/python-flask-petstore.sh +++ b/bin/python-server-flask-petstore-python2.sh @@ -25,11 +25,14 @@ then mvn -B clean package fi -out_folder=samples/server/petstore/flaskConnexion +generator=python-flask +input=modules/openapi-generator/src/test/resources/2_0/petstore.yaml +out_folder=samples/server/petstore/$generator-python2 +resources=modules/openapi-generator/src/main/resources/$generator # if you've executed sbt assembly previously it will use that instead. export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties" -ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o $out_folder -Dservice $@" +ags="generate -t $resources -i $input -g $generator -o $out_folder -c bin/supportPython2.json -D service $@" rm -rf $out_folder/.openapi* rm -rf $out_folder/openapi_server diff --git a/bin/python-flask-petstore-python2.sh b/bin/python-server-flask-petstore.sh similarity index 78% rename from bin/python-flask-petstore-python2.sh rename to bin/python-server-flask-petstore.sh index 5ba265e4cabb..915aacd540ab 100755 --- a/bin/python-flask-petstore-python2.sh +++ b/bin/python-server-flask-petstore.sh @@ -25,11 +25,14 @@ then mvn -B clean package fi -out_folder=samples/server/petstore/flaskConnexion-python2 +generator=python-flask +input=modules/openapi-generator/src/test/resources/2_0/petstore.yaml +out_folder=samples/server/petstore/$generator +resources=modules/openapi-generator/src/main/resources/$generator # if you've executed sbt assembly previously it will use that instead. export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties" -ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o $out_folder -c bin/supportPython2.json -D service $@" +ags="generate -t $resources -i $input -g $generator -o $out_folder -Dservice $@" rm -rf $out_folder/.openapi* rm -rf $out_folder/openapi_server diff --git a/bin/windows/python2-flask-petstore.bat b/bin/windows/python2-flask-petstore.bat index d0999b8e92ef..cc77dfcd5fc5 100755 --- a/bin/windows/python2-flask-petstore.bat +++ b/bin/windows/python2-flask-petstore.bat @@ -5,6 +5,6 @@ If Not Exist %executable% ( ) REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g python-flask -o samples\server\petstore\flaskConnexion-python2 -c bin\supportPython2.json -D service +set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g python-flask -o samples\server\petstore\python-flask-python2 -c bin\supportPython2.json -D service java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/python3-flask-petstore.bat b/bin/windows/python3-flask-petstore.bat index 283c896c2909..c1677662bd0f 100755 --- a/bin/windows/python3-flask-petstore.bat +++ b/bin/windows/python3-flask-petstore.bat @@ -5,6 +5,6 @@ If Not Exist %executable% ( ) REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g python-flask -o samples\server\petstore\flaskConnexion -D service +set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g python-flask -o samples\server\petstore\python-flask -D service java %JAVA_OPTS% -jar %executable% %ags% diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFlaskConnexionServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFlaskConnexionServerCodegen.java index 468d32e0210a..a7aac8d4b6a7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFlaskConnexionServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFlaskConnexionServerCodegen.java @@ -29,7 +29,7 @@ public class PythonFlaskConnexionServerCodegen extends PythonAbstractConnexionSe private static final Logger LOGGER = LoggerFactory.getLogger(PythonFlaskConnexionServerCodegen.class); public PythonFlaskConnexionServerCodegen() { - super("flaskConnexion", false); + super("python-flask", false); } /** diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/Dockerfile.mustache b/modules/openapi-generator/src/main/resources/python-flask/Dockerfile.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/Dockerfile.mustache rename to modules/openapi-generator/src/main/resources/python-flask/Dockerfile.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/README.mustache b/modules/openapi-generator/src/main/resources/python-flask/README.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/README.mustache rename to modules/openapi-generator/src/main/resources/python-flask/README.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/__init__.mustache b/modules/openapi-generator/src/main/resources/python-flask/__init__.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/__init__.mustache rename to modules/openapi-generator/src/main/resources/python-flask/__init__.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/__init__model.mustache b/modules/openapi-generator/src/main/resources/python-flask/__init__model.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/__init__model.mustache rename to modules/openapi-generator/src/main/resources/python-flask/__init__model.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/__init__test.mustache b/modules/openapi-generator/src/main/resources/python-flask/__init__test.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/__init__test.mustache rename to modules/openapi-generator/src/main/resources/python-flask/__init__test.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/__main__.mustache b/modules/openapi-generator/src/main/resources/python-flask/__main__.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/__main__.mustache rename to modules/openapi-generator/src/main/resources/python-flask/__main__.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/base_model_.mustache b/modules/openapi-generator/src/main/resources/python-flask/base_model_.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/base_model_.mustache rename to modules/openapi-generator/src/main/resources/python-flask/base_model_.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/controller.mustache b/modules/openapi-generator/src/main/resources/python-flask/controller.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/controller.mustache rename to modules/openapi-generator/src/main/resources/python-flask/controller.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/controller_test.mustache b/modules/openapi-generator/src/main/resources/python-flask/controller_test.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/controller_test.mustache rename to modules/openapi-generator/src/main/resources/python-flask/controller_test.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/dockerignore.mustache b/modules/openapi-generator/src/main/resources/python-flask/dockerignore.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/dockerignore.mustache rename to modules/openapi-generator/src/main/resources/python-flask/dockerignore.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/encoder.mustache b/modules/openapi-generator/src/main/resources/python-flask/encoder.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/encoder.mustache rename to modules/openapi-generator/src/main/resources/python-flask/encoder.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/git_push.sh.mustache b/modules/openapi-generator/src/main/resources/python-flask/git_push.sh.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/git_push.sh.mustache rename to modules/openapi-generator/src/main/resources/python-flask/git_push.sh.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/gitignore.mustache b/modules/openapi-generator/src/main/resources/python-flask/gitignore.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/gitignore.mustache rename to modules/openapi-generator/src/main/resources/python-flask/gitignore.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/model.mustache b/modules/openapi-generator/src/main/resources/python-flask/model.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/model.mustache rename to modules/openapi-generator/src/main/resources/python-flask/model.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/openapi.mustache b/modules/openapi-generator/src/main/resources/python-flask/openapi.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/openapi.mustache rename to modules/openapi-generator/src/main/resources/python-flask/openapi.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/param_type.mustache b/modules/openapi-generator/src/main/resources/python-flask/param_type.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/param_type.mustache rename to modules/openapi-generator/src/main/resources/python-flask/param_type.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/requirements.mustache b/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/requirements.mustache rename to modules/openapi-generator/src/main/resources/python-flask/requirements.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/security_controller_.mustache b/modules/openapi-generator/src/main/resources/python-flask/security_controller_.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/security_controller_.mustache rename to modules/openapi-generator/src/main/resources/python-flask/security_controller_.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/setup.mustache b/modules/openapi-generator/src/main/resources/python-flask/setup.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/setup.mustache rename to modules/openapi-generator/src/main/resources/python-flask/setup.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/test-requirements.mustache b/modules/openapi-generator/src/main/resources/python-flask/test-requirements.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/test-requirements.mustache rename to modules/openapi-generator/src/main/resources/python-flask/test-requirements.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/tox.mustache b/modules/openapi-generator/src/main/resources/python-flask/tox.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/tox.mustache rename to modules/openapi-generator/src/main/resources/python-flask/tox.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/travis.mustache b/modules/openapi-generator/src/main/resources/python-flask/travis.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/travis.mustache rename to modules/openapi-generator/src/main/resources/python-flask/travis.mustache diff --git a/modules/openapi-generator/src/main/resources/flaskConnexion/util.mustache b/modules/openapi-generator/src/main/resources/python-flask/util.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/flaskConnexion/util.mustache rename to modules/openapi-generator/src/main/resources/python-flask/util.mustache diff --git a/pom.xml b/pom.xml index 7e3785be228b..ae53ac825040 100644 --- a/pom.xml +++ b/pom.xml @@ -1057,8 +1057,8 @@ samples/server/petstore/rust-server samples/server/petstore/python-aiohttp - samples/server/petstore/flaskConnexion - samples/server/petstore/flaskConnexion-python2 + samples/server/petstore/python-flask + samples/server/petstore/python-flask-python2 diff --git a/samples/server/petstore/flaskConnexion-python2/.dockerignore b/samples/server/petstore/python-flask-python2/.dockerignore similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/.dockerignore rename to samples/server/petstore/python-flask-python2/.dockerignore diff --git a/samples/server/petstore/flaskConnexion-python2/.gitignore b/samples/server/petstore/python-flask-python2/.gitignore similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/.gitignore rename to samples/server/petstore/python-flask-python2/.gitignore diff --git a/samples/server/petstore/flaskConnexion-python2/.openapi-generator-ignore b/samples/server/petstore/python-flask-python2/.openapi-generator-ignore similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/.openapi-generator-ignore rename to samples/server/petstore/python-flask-python2/.openapi-generator-ignore diff --git a/samples/server/petstore/flaskConnexion-python2/.openapi-generator/VERSION b/samples/server/petstore/python-flask-python2/.openapi-generator/VERSION similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/.openapi-generator/VERSION rename to samples/server/petstore/python-flask-python2/.openapi-generator/VERSION diff --git a/samples/server/petstore/flaskConnexion-python2/.travis.yml b/samples/server/petstore/python-flask-python2/.travis.yml similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/.travis.yml rename to samples/server/petstore/python-flask-python2/.travis.yml diff --git a/samples/server/petstore/flaskConnexion-python2/Dockerfile b/samples/server/petstore/python-flask-python2/Dockerfile similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/Dockerfile rename to samples/server/petstore/python-flask-python2/Dockerfile diff --git a/samples/server/petstore/flaskConnexion-python2/Makefile b/samples/server/petstore/python-flask-python2/Makefile similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/Makefile rename to samples/server/petstore/python-flask-python2/Makefile diff --git a/samples/server/petstore/flaskConnexion-python2/README.md b/samples/server/petstore/python-flask-python2/README.md similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/README.md rename to samples/server/petstore/python-flask-python2/README.md diff --git a/samples/server/petstore/flaskConnexion-python2/git_push.sh b/samples/server/petstore/python-flask-python2/git_push.sh similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/git_push.sh rename to samples/server/petstore/python-flask-python2/git_push.sh diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/__init__.py b/samples/server/petstore/python-flask-python2/openapi_server/__init__.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/__init__.py rename to samples/server/petstore/python-flask-python2/openapi_server/__init__.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/__main__.py b/samples/server/petstore/python-flask-python2/openapi_server/__main__.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/__main__.py rename to samples/server/petstore/python-flask-python2/openapi_server/__main__.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/__init__.py b/samples/server/petstore/python-flask-python2/openapi_server/controllers/__init__.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/__init__.py rename to samples/server/petstore/python-flask-python2/openapi_server/controllers/__init__.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/pet_controller.py b/samples/server/petstore/python-flask-python2/openapi_server/controllers/pet_controller.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/pet_controller.py rename to samples/server/petstore/python-flask-python2/openapi_server/controllers/pet_controller.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/security_controller_.py b/samples/server/petstore/python-flask-python2/openapi_server/controllers/security_controller_.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/security_controller_.py rename to samples/server/petstore/python-flask-python2/openapi_server/controllers/security_controller_.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/store_controller.py b/samples/server/petstore/python-flask-python2/openapi_server/controllers/store_controller.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/store_controller.py rename to samples/server/petstore/python-flask-python2/openapi_server/controllers/store_controller.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/user_controller.py b/samples/server/petstore/python-flask-python2/openapi_server/controllers/user_controller.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/controllers/user_controller.py rename to samples/server/petstore/python-flask-python2/openapi_server/controllers/user_controller.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/encoder.py b/samples/server/petstore/python-flask-python2/openapi_server/encoder.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/encoder.py rename to samples/server/petstore/python-flask-python2/openapi_server/encoder.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/models/__init__.py b/samples/server/petstore/python-flask-python2/openapi_server/models/__init__.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/models/__init__.py rename to samples/server/petstore/python-flask-python2/openapi_server/models/__init__.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/models/api_response.py b/samples/server/petstore/python-flask-python2/openapi_server/models/api_response.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/models/api_response.py rename to samples/server/petstore/python-flask-python2/openapi_server/models/api_response.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/models/base_model_.py b/samples/server/petstore/python-flask-python2/openapi_server/models/base_model_.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/models/base_model_.py rename to samples/server/petstore/python-flask-python2/openapi_server/models/base_model_.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/models/category.py b/samples/server/petstore/python-flask-python2/openapi_server/models/category.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/models/category.py rename to samples/server/petstore/python-flask-python2/openapi_server/models/category.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/models/order.py b/samples/server/petstore/python-flask-python2/openapi_server/models/order.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/models/order.py rename to samples/server/petstore/python-flask-python2/openapi_server/models/order.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/models/pet.py b/samples/server/petstore/python-flask-python2/openapi_server/models/pet.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/models/pet.py rename to samples/server/petstore/python-flask-python2/openapi_server/models/pet.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/models/tag.py b/samples/server/petstore/python-flask-python2/openapi_server/models/tag.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/models/tag.py rename to samples/server/petstore/python-flask-python2/openapi_server/models/tag.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/models/user.py b/samples/server/petstore/python-flask-python2/openapi_server/models/user.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/models/user.py rename to samples/server/petstore/python-flask-python2/openapi_server/models/user.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/openapi/openapi.yaml b/samples/server/petstore/python-flask-python2/openapi_server/openapi/openapi.yaml similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/openapi/openapi.yaml rename to samples/server/petstore/python-flask-python2/openapi_server/openapi/openapi.yaml diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/__init__.py b/samples/server/petstore/python-flask-python2/openapi_server/test/__init__.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/test/__init__.py rename to samples/server/petstore/python-flask-python2/openapi_server/test/__init__.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_pet_controller.py b/samples/server/petstore/python-flask-python2/openapi_server/test/test_pet_controller.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_pet_controller.py rename to samples/server/petstore/python-flask-python2/openapi_server/test/test_pet_controller.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_store_controller.py b/samples/server/petstore/python-flask-python2/openapi_server/test/test_store_controller.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_store_controller.py rename to samples/server/petstore/python-flask-python2/openapi_server/test/test_store_controller.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_user_controller.py b/samples/server/petstore/python-flask-python2/openapi_server/test/test_user_controller.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/test/test_user_controller.py rename to samples/server/petstore/python-flask-python2/openapi_server/test/test_user_controller.py diff --git a/samples/server/petstore/flaskConnexion-python2/openapi_server/util.py b/samples/server/petstore/python-flask-python2/openapi_server/util.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/openapi_server/util.py rename to samples/server/petstore/python-flask-python2/openapi_server/util.py diff --git a/samples/server/petstore/flaskConnexion-python2/pom.xml b/samples/server/petstore/python-flask-python2/pom.xml similarity index 96% rename from samples/server/petstore/flaskConnexion-python2/pom.xml rename to samples/server/petstore/python-flask-python2/pom.xml index 04b4d23a1be7..113c387d5f21 100644 --- a/samples/server/petstore/flaskConnexion-python2/pom.xml +++ b/samples/server/petstore/python-flask-python2/pom.xml @@ -1,7 +1,7 @@ 4.0.0 org.openapitools - FlaskConnexionTestsPython2 + PythonFlaskConnexionTestsPython2 pom 1.0-SNAPSHOT Python Flask Server python 2 diff --git a/samples/server/petstore/flaskConnexion-python2/requirements.txt b/samples/server/petstore/python-flask-python2/requirements.txt similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/requirements.txt rename to samples/server/petstore/python-flask-python2/requirements.txt diff --git a/samples/server/petstore/flaskConnexion-python2/setup.py b/samples/server/petstore/python-flask-python2/setup.py similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/setup.py rename to samples/server/petstore/python-flask-python2/setup.py diff --git a/samples/server/petstore/flaskConnexion-python2/test-requirements.txt b/samples/server/petstore/python-flask-python2/test-requirements.txt similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/test-requirements.txt rename to samples/server/petstore/python-flask-python2/test-requirements.txt diff --git a/samples/server/petstore/flaskConnexion-python2/test_python2.sh b/samples/server/petstore/python-flask-python2/test_python2.sh similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/test_python2.sh rename to samples/server/petstore/python-flask-python2/test_python2.sh diff --git a/samples/server/petstore/flaskConnexion-python2/tox.ini b/samples/server/petstore/python-flask-python2/tox.ini similarity index 100% rename from samples/server/petstore/flaskConnexion-python2/tox.ini rename to samples/server/petstore/python-flask-python2/tox.ini diff --git a/samples/server/petstore/flaskConnexion/.dockerignore b/samples/server/petstore/python-flask/.dockerignore similarity index 100% rename from samples/server/petstore/flaskConnexion/.dockerignore rename to samples/server/petstore/python-flask/.dockerignore diff --git a/samples/server/petstore/flaskConnexion/.gitignore b/samples/server/petstore/python-flask/.gitignore similarity index 100% rename from samples/server/petstore/flaskConnexion/.gitignore rename to samples/server/petstore/python-flask/.gitignore diff --git a/samples/server/petstore/flaskConnexion/.openapi-generator-ignore b/samples/server/petstore/python-flask/.openapi-generator-ignore similarity index 100% rename from samples/server/petstore/flaskConnexion/.openapi-generator-ignore rename to samples/server/petstore/python-flask/.openapi-generator-ignore diff --git a/samples/server/petstore/flaskConnexion/.openapi-generator/VERSION b/samples/server/petstore/python-flask/.openapi-generator/VERSION similarity index 100% rename from samples/server/petstore/flaskConnexion/.openapi-generator/VERSION rename to samples/server/petstore/python-flask/.openapi-generator/VERSION diff --git a/samples/server/petstore/flaskConnexion/.travis.yml b/samples/server/petstore/python-flask/.travis.yml similarity index 100% rename from samples/server/petstore/flaskConnexion/.travis.yml rename to samples/server/petstore/python-flask/.travis.yml diff --git a/samples/server/petstore/flaskConnexion/Dockerfile b/samples/server/petstore/python-flask/Dockerfile similarity index 100% rename from samples/server/petstore/flaskConnexion/Dockerfile rename to samples/server/petstore/python-flask/Dockerfile diff --git a/samples/server/petstore/flaskConnexion/Makefile b/samples/server/petstore/python-flask/Makefile similarity index 100% rename from samples/server/petstore/flaskConnexion/Makefile rename to samples/server/petstore/python-flask/Makefile diff --git a/samples/server/petstore/flaskConnexion/README.md b/samples/server/petstore/python-flask/README.md similarity index 100% rename from samples/server/petstore/flaskConnexion/README.md rename to samples/server/petstore/python-flask/README.md diff --git a/samples/server/petstore/flaskConnexion/git_push.sh b/samples/server/petstore/python-flask/git_push.sh similarity index 100% rename from samples/server/petstore/flaskConnexion/git_push.sh rename to samples/server/petstore/python-flask/git_push.sh diff --git a/samples/server/petstore/flaskConnexion/openapi_server/__init__.py b/samples/server/petstore/python-flask/openapi_server/__init__.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/__init__.py rename to samples/server/petstore/python-flask/openapi_server/__init__.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/__main__.py b/samples/server/petstore/python-flask/openapi_server/__main__.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/__main__.py rename to samples/server/petstore/python-flask/openapi_server/__main__.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/controllers/__init__.py b/samples/server/petstore/python-flask/openapi_server/controllers/__init__.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/controllers/__init__.py rename to samples/server/petstore/python-flask/openapi_server/controllers/__init__.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/controllers/pet_controller.py b/samples/server/petstore/python-flask/openapi_server/controllers/pet_controller.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/controllers/pet_controller.py rename to samples/server/petstore/python-flask/openapi_server/controllers/pet_controller.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/controllers/security_controller_.py b/samples/server/petstore/python-flask/openapi_server/controllers/security_controller_.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/controllers/security_controller_.py rename to samples/server/petstore/python-flask/openapi_server/controllers/security_controller_.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/controllers/store_controller.py b/samples/server/petstore/python-flask/openapi_server/controllers/store_controller.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/controllers/store_controller.py rename to samples/server/petstore/python-flask/openapi_server/controllers/store_controller.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/controllers/user_controller.py b/samples/server/petstore/python-flask/openapi_server/controllers/user_controller.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/controllers/user_controller.py rename to samples/server/petstore/python-flask/openapi_server/controllers/user_controller.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/encoder.py b/samples/server/petstore/python-flask/openapi_server/encoder.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/encoder.py rename to samples/server/petstore/python-flask/openapi_server/encoder.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/models/__init__.py b/samples/server/petstore/python-flask/openapi_server/models/__init__.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/models/__init__.py rename to samples/server/petstore/python-flask/openapi_server/models/__init__.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/models/api_response.py b/samples/server/petstore/python-flask/openapi_server/models/api_response.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/models/api_response.py rename to samples/server/petstore/python-flask/openapi_server/models/api_response.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/models/base_model_.py b/samples/server/petstore/python-flask/openapi_server/models/base_model_.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/models/base_model_.py rename to samples/server/petstore/python-flask/openapi_server/models/base_model_.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/models/category.py b/samples/server/petstore/python-flask/openapi_server/models/category.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/models/category.py rename to samples/server/petstore/python-flask/openapi_server/models/category.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/models/order.py b/samples/server/petstore/python-flask/openapi_server/models/order.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/models/order.py rename to samples/server/petstore/python-flask/openapi_server/models/order.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/models/pet.py b/samples/server/petstore/python-flask/openapi_server/models/pet.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/models/pet.py rename to samples/server/petstore/python-flask/openapi_server/models/pet.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/models/tag.py b/samples/server/petstore/python-flask/openapi_server/models/tag.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/models/tag.py rename to samples/server/petstore/python-flask/openapi_server/models/tag.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/models/user.py b/samples/server/petstore/python-flask/openapi_server/models/user.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/models/user.py rename to samples/server/petstore/python-flask/openapi_server/models/user.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/openapi/openapi.yaml b/samples/server/petstore/python-flask/openapi_server/openapi/openapi.yaml similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/openapi/openapi.yaml rename to samples/server/petstore/python-flask/openapi_server/openapi/openapi.yaml diff --git a/samples/server/petstore/flaskConnexion/openapi_server/test/__init__.py b/samples/server/petstore/python-flask/openapi_server/test/__init__.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/test/__init__.py rename to samples/server/petstore/python-flask/openapi_server/test/__init__.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/test/test_pet_controller.py b/samples/server/petstore/python-flask/openapi_server/test/test_pet_controller.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/test/test_pet_controller.py rename to samples/server/petstore/python-flask/openapi_server/test/test_pet_controller.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/test/test_store_controller.py b/samples/server/petstore/python-flask/openapi_server/test/test_store_controller.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/test/test_store_controller.py rename to samples/server/petstore/python-flask/openapi_server/test/test_store_controller.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/test/test_user_controller.py b/samples/server/petstore/python-flask/openapi_server/test/test_user_controller.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/test/test_user_controller.py rename to samples/server/petstore/python-flask/openapi_server/test/test_user_controller.py diff --git a/samples/server/petstore/flaskConnexion/openapi_server/util.py b/samples/server/petstore/python-flask/openapi_server/util.py similarity index 100% rename from samples/server/petstore/flaskConnexion/openapi_server/util.py rename to samples/server/petstore/python-flask/openapi_server/util.py diff --git a/samples/server/petstore/flaskConnexion/pom.xml b/samples/server/petstore/python-flask/pom.xml similarity index 96% rename from samples/server/petstore/flaskConnexion/pom.xml rename to samples/server/petstore/python-flask/pom.xml index 2fee39a223d5..2834d92dbc7a 100644 --- a/samples/server/petstore/flaskConnexion/pom.xml +++ b/samples/server/petstore/python-flask/pom.xml @@ -1,7 +1,7 @@ 4.0.0 org.openapitools - FlaskConnexionTests + PythonFlaskConnexionTests pom 1.0-SNAPSHOT Python Flask Server diff --git a/samples/server/petstore/flaskConnexion/requirements.txt b/samples/server/petstore/python-flask/requirements.txt similarity index 100% rename from samples/server/petstore/flaskConnexion/requirements.txt rename to samples/server/petstore/python-flask/requirements.txt diff --git a/samples/server/petstore/flaskConnexion/setup.py b/samples/server/petstore/python-flask/setup.py similarity index 100% rename from samples/server/petstore/flaskConnexion/setup.py rename to samples/server/petstore/python-flask/setup.py diff --git a/samples/server/petstore/flaskConnexion/test-requirements.txt b/samples/server/petstore/python-flask/test-requirements.txt similarity index 100% rename from samples/server/petstore/flaskConnexion/test-requirements.txt rename to samples/server/petstore/python-flask/test-requirements.txt diff --git a/samples/server/petstore/flaskConnexion/test_python3.sh b/samples/server/petstore/python-flask/test_python3.sh similarity index 100% rename from samples/server/petstore/flaskConnexion/test_python3.sh rename to samples/server/petstore/python-flask/test_python3.sh diff --git a/samples/server/petstore/flaskConnexion/tox.ini b/samples/server/petstore/python-flask/tox.ini similarity index 100% rename from samples/server/petstore/flaskConnexion/tox.ini rename to samples/server/petstore/python-flask/tox.ini