From 76d54f7f400a6a2f0338df5aa345e7c765f79179 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Mon, 24 Feb 2020 17:58:41 -0600 Subject: [PATCH 01/20] python server: Add pythonSrcRoot option This will allow the python project to be in a subdirectory (as specified in pythonSrcRoot). That could mean following the src layout with sources under src/ or lib/. Multi-language projects might use a sub directory like python/, or whatever makes sense for the project. By default, the pythonSrcRoot is "", meaning the existing behavior is the default. --- .../PythonAbstractConnexionServerCodegen.java | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) 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 18256382d585..38ed47d4e66f 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 @@ -51,6 +51,7 @@ public class PythonAbstractConnexionServerCodegen extends DefaultCodegen impleme public static final String SUPPORT_PYTHON2 = "supportPython2"; // nose is a python testing framework, we use pytest if USE_NOSE is unset public static final String USE_NOSE = "useNose"; + public static final String PYTHON_SRC_ROOT = "pythonSrcRoot"; static final String MEDIA_TYPE = "mediaType"; protected int serverPort = 8080; @@ -61,6 +62,7 @@ public class PythonAbstractConnexionServerCodegen extends DefaultCodegen impleme protected Map regexModifiers; protected boolean fixBodyName; protected boolean useNose = Boolean.FALSE; + protected String pythonSrcRoot; public PythonAbstractConnexionServerCodegen(String templateDirectory, boolean fixBodyNameValue) { super(); @@ -165,6 +167,8 @@ public PythonAbstractConnexionServerCodegen(String templateDirectory, boolean fi defaultValue("8080")); cliOptions.add(CliOption.newBoolean(USE_NOSE, "use the nose test framework"). defaultValue(Boolean.FALSE.toString())); + cliOptions.add(new CliOption(PYTHON_SRC_ROOT, "put python sources in this subdirectory of output folder (defaults to \"\" for). Use this for src/ layout."). + defaultValue("")); } protected void addSupportingFiles() { @@ -212,6 +216,12 @@ public void processOpts() { if (additionalProperties.containsKey(USE_NOSE)) { setUseNose((String) additionalProperties.get(USE_NOSE)); } + if (additionalProperties.containsKey(PYTHON_SRC_ROOT)) { + setPythonSrcRoot((String) additionalProperties.get(PYTHON_SRC_ROOT)); + additionalProperties.put(PYTHON_SRC_ROOT, pythonSrcRoot); + } else { + setPythonSrcRoot(""); + } supportingFiles.add(new SupportingFile("__main__.mustache", packagePath(), "__main__.py")); supportingFiles.add(new SupportingFile("util.mustache", packagePath(), "util.py")); supportingFiles.add(new SupportingFile("typing_utils.mustache", packagePath(), "typing_utils.py")); @@ -230,6 +240,25 @@ public void setUseNose(String val) { this.useNose = Boolean.valueOf(val); } + public void setPythonSrcRoot(String val) { + String pySrcRoot; + if (val == null) { + pySrcRoot = ""; + } else { + pySrcRoot = val.replaceAll("[/\\\\]+$", ""); + } + + if (pySrcRoot.isEmpty() || pySrcRoot == ".") { + this.pythonSrcRoot = ""; + } else { + this.pythonSrcRoot = pySrcRoot + File.separator; + } + } + + public String pythonSrcOutputFolder() { + return outputFolder + File.separator + pythonSrcRoot; + } + private static String packageToPath(String pkg) { return pkg.replace(".", File.separator); } @@ -304,7 +333,14 @@ public String escapeReservedWord(String name) { */ @Override public String apiFileFolder() { - return outputFolder + File.separator + apiPackage().replace('.', File.separatorChar); + String pkgPath = apiPackage().replace('.', File.separatorChar); + return pythonSrcOutputFolder() + pkgPath; + } + + @Override + public String modelFileFolder() { + String pkgPath = modelPackage().replace('.', File.separatorChar); + return pythonSrcOutputFolder() + pkgPath; } @Override @@ -799,7 +835,8 @@ public void setPackageVersion(String packageVersion) { } public String packagePath() { - return packageName.replace('.', File.separatorChar); + String pkgPath = packageName.replace('.', File.separatorChar); + return pythonSrcRoot + pkgPath; } @Override From fd751e9efd4413fc50d5233b1504ba71f3158a43 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 6 Mar 2020 00:29:08 -0600 Subject: [PATCH 02/20] python server: update template files to support pythonSrcRoot --- .../src/main/resources/python-aiohttp/tox.mustache | 2 +- .../src/main/resources/python-blueplanet/app/setup.mustache | 3 ++- .../src/main/resources/python-blueplanet/app/tox.mustache | 2 +- .../src/main/resources/python-blueplanet/tox.mustache | 0 .../src/main/resources/python-flask/setup.mustache | 3 ++- .../src/main/resources/python-flask/tox.mustache | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/python-blueplanet/tox.mustache diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/tox.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/tox.mustache index 87cc65eeeb78..852319368691 100644 --- a/modules/openapi-generator/src/main/resources/python-aiohttp/tox.mustache +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/tox.mustache @@ -7,4 +7,4 @@ deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands= - {{^useNose}}pytest --cov={{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}} \ No newline at end of file + {{^useNose}}pytest --cov={{{pythonSrcRoot}}}{{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/python-blueplanet/app/setup.mustache b/modules/openapi-generator/src/main/resources/python-blueplanet/app/setup.mustache index 56f8bc1ec176..5aff19b4491c 100644 --- a/modules/openapi-generator/src/main/resources/python-blueplanet/app/setup.mustache +++ b/modules/openapi-generator/src/main/resources/python-blueplanet/app/setup.mustache @@ -23,7 +23,8 @@ setup( url="{{packageUrl}}", keywords=["Swagger", "{{appName}}"], install_requires=REQUIRES, - packages=find_packages(), + packages=find_packages({{#pythonSrcRoot}}"{{{.}}}"{{/pythonSrcRoot}}),{{#pythonSrcRoot}} + package_dir={"": "{{{.}}}"},{{/pythonSrcRoot}} package_data={'': ['swagger/swagger.yaml']}, include_package_data=True, entry_points={ diff --git a/modules/openapi-generator/src/main/resources/python-blueplanet/app/tox.mustache b/modules/openapi-generator/src/main/resources/python-blueplanet/app/tox.mustache index 7b3246c36e25..d774cfd8dbdc 100644 --- a/modules/openapi-generator/src/main/resources/python-blueplanet/app/tox.mustache +++ b/modules/openapi-generator/src/main/resources/python-blueplanet/app/tox.mustache @@ -6,4 +6,4 @@ deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands= - {{^useNose}}pytest --cov={{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}} \ No newline at end of file + {{^useNose}}pytest --cov={{{pythonSrcRoot}}}{{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/python-blueplanet/tox.mustache b/modules/openapi-generator/src/main/resources/python-blueplanet/tox.mustache new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/modules/openapi-generator/src/main/resources/python-flask/setup.mustache b/modules/openapi-generator/src/main/resources/python-flask/setup.mustache index bda71175d0da..4d562b787356 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/setup.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/setup.mustache @@ -28,7 +28,8 @@ setup( url="{{packageUrl}}", keywords=["OpenAPI", "{{appName}}"], install_requires=REQUIRES, - packages=find_packages(), + packages=find_packages({{#pythonSrcRoot}}"{{{.}}}"{{/pythonSrcRoot}}),{{#pythonSrcRoot}} + package_dir={"": "{{{.}}}"},{{/pythonSrcRoot}} package_data={'': ['openapi/openapi.yaml']}, include_package_data=True, entry_points={ diff --git a/modules/openapi-generator/src/main/resources/python-flask/tox.mustache b/modules/openapi-generator/src/main/resources/python-flask/tox.mustache index 7fcd185a8d82..95a527aafb6e 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/tox.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/tox.mustache @@ -6,4 +6,4 @@ deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands= - {{^useNose}}pytest --cov={{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}} \ No newline at end of file + {{^useNose}}pytest --cov={{{pythonSrcRoot}}}{{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}} \ No newline at end of file From 7df485f284f1e6623c0f47c1e34f9fe3512b8bb3 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 26 Feb 2020 16:09:06 -0600 Subject: [PATCH 03/20] python server: update docs to add pythonSrcRoot option --- docs/generators/python-aiohttp.md | 1 + docs/generators/python-blueplanet.md | 1 + docs/generators/python-flask.md | 1 + 3 files changed, 3 insertions(+) diff --git a/docs/generators/python-aiohttp.md b/docs/generators/python-aiohttp.md index 5f71e7b0b5dd..ee337d2e5bb2 100644 --- a/docs/generators/python-aiohttp.md +++ b/docs/generators/python-aiohttp.md @@ -12,6 +12,7 @@ sidebar_label: python-aiohttp |packageName|python package name (convention: snake_case).| |openapi_server| |packageVersion|python package version.| |1.0.0| |prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false| +|pythonSrcRoot|put python sources in this subdirectory of output folder (defaults to "" for). Use this for src/ layout.| || |serverPort|TCP port to listen to in app.run| |8080| |sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true| |sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true| diff --git a/docs/generators/python-blueplanet.md b/docs/generators/python-blueplanet.md index 356d5847fa7b..0382237eef5c 100644 --- a/docs/generators/python-blueplanet.md +++ b/docs/generators/python-blueplanet.md @@ -12,6 +12,7 @@ sidebar_label: python-blueplanet |packageName|python package name (convention: snake_case).| |openapi_server| |packageVersion|python package version.| |1.0.0| |prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false| +|pythonSrcRoot|put python sources in this subdirectory of output folder (defaults to "" for). Use this for src/ layout.| || |serverPort|TCP port to listen to in app.run| |8080| |sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true| |sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true| diff --git a/docs/generators/python-flask.md b/docs/generators/python-flask.md index a800c98ce3bc..c8ab554adeba 100644 --- a/docs/generators/python-flask.md +++ b/docs/generators/python-flask.md @@ -12,6 +12,7 @@ sidebar_label: python-flask |packageName|python package name (convention: snake_case).| |openapi_server| |packageVersion|python package version.| |1.0.0| |prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false| +|pythonSrcRoot|put python sources in this subdirectory of output folder (defaults to "" for). Use this for src/ layout.| || |serverPort|TCP port to listen to in app.run| |8080| |sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true| |sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true| From 9e7a2a32cce3fa69510a0b6722d00614998f2c33 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Mon, 24 Feb 2020 22:33:56 -0600 Subject: [PATCH 04/20] python server: add pythonSrcRoot sample script --- ...ython-server-aiohttp-srclayout-petstore.sh | 44 +++++++++++++++++++ bin/python-server-all.sh | 1 + 2 files changed, 45 insertions(+) create mode 100755 bin/python-server-aiohttp-srclayout-petstore.sh diff --git a/bin/python-server-aiohttp-srclayout-petstore.sh b/bin/python-server-aiohttp-srclayout-petstore.sh new file mode 100755 index 000000000000..126e2d9fcc13 --- /dev/null +++ b/bin/python-server-aiohttp-srclayout-petstore.sh @@ -0,0 +1,44 @@ +#!/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 + +generator=python-aiohttp +input=modules/openapi-generator/src/test/resources/2_0/petstore.yaml +out_folder=samples/server/petstore/${generator}-srclayout +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 -Dservice" +ags="generate -t $resources -i $input -g $generator -o $out_folder --additional-properties pythonSrcRoot=src $@" + +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-server-all.sh b/bin/python-server-all.sh index 5fe3176eed9e..c4fa204d1c6a 100755 --- a/bin/python-server-all.sh +++ b/bin/python-server-all.sh @@ -1,6 +1,7 @@ #!/bin/sh ./bin/python-server-aiohttp-petstore.sh +./bin/python-server-aiohttp-srclayout-petstore.sh ./bin/python-server-flask-petstore.sh ./bin/python-server-flask-petstore-python2.sh ./bin/python-server-blueplanet-petstore.sh From a440404e1629a066e6c6872c3c325666381a85fb Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Mon, 24 Feb 2020 23:52:57 -0600 Subject: [PATCH 05/20] python server: build sample srclayout project --- .../python-aiohttp-srclayout/.gitignore | 66 ++ .../.openapi-generator-ignore | 23 + .../.openapi-generator/VERSION | 1 + .../python-aiohttp-srclayout/README.md | 46 + .../python-aiohttp-srclayout/requirements.txt | 3 + .../src/openapi_server/__init__.py | 15 + .../src/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 +++ .../src/openapi_server/models/__init__.py | 9 + .../src/openapi_server/models/api_response.py | 110 +++ .../src/openapi_server/models/base_model_.py | 66 ++ .../src/openapi_server/models/category.py | 85 ++ .../src/openapi_server/models/order.py | 193 +++++ .../src/openapi_server/models/pet.py | 199 +++++ .../src/openapi_server/models/tag.py | 85 ++ .../src/openapi_server/models/user.py | 237 ++++++ .../src/openapi_server/openapi/openapi.yaml | 796 ++++++++++++++++++ .../src/openapi_server/typing_utils.py | 32 + .../src/openapi_server/util.py | 131 +++ .../test-requirements.txt | 4 + .../tests/__init__.py | 0 .../tests/conftest.py | 21 + .../tests/test_pet_controller.py | 200 +++++ .../tests/test_store_controller.py | 76 ++ .../tests/test_user_controller.py | 149 ++++ .../petstore/python-aiohttp-srclayout/tox.ini | 10 + 30 files changed, 2865 insertions(+) create mode 100644 samples/server/petstore/python-aiohttp-srclayout/.gitignore create mode 100644 samples/server/petstore/python-aiohttp-srclayout/.openapi-generator-ignore create mode 100644 samples/server/petstore/python-aiohttp-srclayout/.openapi-generator/VERSION create mode 100644 samples/server/petstore/python-aiohttp-srclayout/README.md create mode 100644 samples/server/petstore/python-aiohttp-srclayout/requirements.txt create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/__init__.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/__main__.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/controllers/__init__.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/controllers/pet_controller.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/controllers/security_controller_.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/controllers/store_controller.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/controllers/user_controller.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/__init__.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/api_response.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/base_model_.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/category.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/order.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/pet.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/tag.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/user.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/openapi/openapi.yaml create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/typing_utils.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/util.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/test-requirements.txt create mode 100644 samples/server/petstore/python-aiohttp-srclayout/tests/__init__.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/tests/conftest.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/tests/test_pet_controller.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/tests/test_store_controller.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/tests/test_user_controller.py create mode 100644 samples/server/petstore/python-aiohttp-srclayout/tox.ini diff --git a/samples/server/petstore/python-aiohttp-srclayout/.gitignore b/samples/server/petstore/python-aiohttp-srclayout/.gitignore new file mode 100644 index 000000000000..43995bd42fa2 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/.gitignore @@ -0,0 +1,66 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ +venv/ +.venv/ +.python-version +.pytest_cache + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +#Ipython Notebook +.ipynb_checkpoints diff --git a/samples/server/petstore/python-aiohttp-srclayout/.openapi-generator-ignore b/samples/server/petstore/python-aiohttp-srclayout/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/.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-srclayout/.openapi-generator/VERSION b/samples/server/petstore/python-aiohttp-srclayout/.openapi-generator/VERSION new file mode 100644 index 000000000000..bfbf77eb7fad --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/.openapi-generator/VERSION @@ -0,0 +1 @@ +4.3.0-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/python-aiohttp-srclayout/README.md b/samples/server/petstore/python-aiohttp-srclayout/README.md new file mode 100644 index 000000000000..6aa7491711b1 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/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 aiohttp server. + +This example uses the [Connexion](https://github.com/zalando/connexion) library on top of aiohttp. + +## 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-srclayout/requirements.txt b/samples/server/petstore/python-aiohttp-srclayout/requirements.txt new file mode 100644 index 000000000000..835c75de58a6 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/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-srclayout/src/openapi_server/__init__.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/__init__.py new file mode 100644 index 000000000000..d9fcbb5db20f --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/__init__.py @@ -0,0 +1,15 @@ +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': 'OpenAPI Petstore'}, + pythonic_params=True, + pass_context_arg_name='request') + app.run(port=8080) diff --git a/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/__main__.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/__main__.py new file mode 100644 index 000000000000..f20ae81db34e --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/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-srclayout/src/openapi_server/controllers/__init__.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/controllers/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/controllers/pet_controller.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/controllers/pet_controller.py new file mode 100644 index 000000000000..ad7557832bab --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/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-srclayout/src/openapi_server/controllers/security_controller_.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/controllers/security_controller_.py new file mode 100644 index 000000000000..90ce5c351a1e --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/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 or does not allow access to called API. + """ + return {'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 or does not allow access to called API. + """ + 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-srclayout/src/openapi_server/controllers/store_controller.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/controllers/store_controller.py new file mode 100644 index 000000000000..80512d357f26 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/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-srclayout/src/openapi_server/controllers/user_controller.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/controllers/user_controller.py new file mode 100644 index 000000000000..89dd08724136 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/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-srclayout/src/openapi_server/models/__init__.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/__init__.py new file mode 100644 index 000000000000..8b108a5fe89a --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/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-srclayout/src/openapi_server/models/api_response.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/api_response.py new file mode 100644 index 000000000000..2fc7342b8bc2 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/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-srclayout/src/openapi_server/models/base_model_.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/base_model_.py new file mode 100644 index 000000000000..54f342e56705 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/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-srclayout/src/openapi_server/models/category.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/category.py new file mode 100644 index 000000000000..930eef70829a --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/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-srclayout/src/openapi_server/models/order.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/order.py new file mode 100644 index 000000000000..88df4eb7e8f3 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/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"] # noqa: E501 + 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-srclayout/src/openapi_server/models/pet.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/pet.py new file mode 100644 index 000000000000..d3e68e105e3a --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/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"] # noqa: E501 + 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-srclayout/src/openapi_server/models/tag.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/tag.py new file mode 100644 index 000000000000..d494277441c7 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/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-srclayout/src/openapi_server/models/user.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/models/user.py new file mode 100644 index 000000000000..24e024c54ab5 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/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-srclayout/src/openapi_server/openapi/openapi.yaml b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/openapi/openapi.yaml new file mode 100644 index 000000000000..030b84f7402c --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/openapi/openapi.yaml @@ -0,0 +1,796 @@ +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: https://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/{petId}: + delete: + operationId: delete_pet + parameters: + - in: header + name: api_key + schema: + type: string + - description: Pet id to delete + in: path + name: petId + 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: petId + 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: petId + 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/{petId}/uploadImage: + post: + operationId: upload_file + parameters: + - description: ID of pet to update + in: path + name: petId + 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/{orderId}: + 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 + 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: orderId + 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-srclayout/src/openapi_server/typing_utils.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/typing_utils.py new file mode 100644 index 000000000000..0563f81fd534 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/typing_utils.py @@ -0,0 +1,32 @@ +# coding: utf-8 + +import sys + +if sys.version_info < (3, 7): + import typing + + def is_generic(klass): + """ Determine whether klass is a generic class """ + return type(klass) == typing.GenericMeta + + def is_dict(klass): + """ Determine whether klass is a Dict """ + return klass.__extra__ == dict + + def is_list(klass): + """ Determine whether klass is a List """ + return klass.__extra__ == list + +else: + + def is_generic(klass): + """ Determine whether klass is a generic class """ + return hasattr(klass, '__origin__') + + def is_dict(klass): + """ Determine whether klass is a Dict """ + return klass.__origin__ == dict + + def is_list(klass): + """ Determine whether klass is a List """ + return klass.__origin__ == list diff --git a/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/util.py b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/util.py new file mode 100644 index 000000000000..c446943677ea --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/util.py @@ -0,0 +1,131 @@ +import datetime + +import typing +from typing import Union +from openapi_server import typing_utils + +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 typing_utils.is_generic(klass): + if typing_utils.is_list(klass): + return _deserialize_list(data, klass.__args__[0]) + if typing_utils.is_dict(klass): + 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-srclayout/test-requirements.txt b/samples/server/petstore/python-aiohttp-srclayout/test-requirements.txt new file mode 100644 index 000000000000..31b28baaf284 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/test-requirements.txt @@ -0,0 +1,4 @@ +pytest~=4.6.7 # needed for python 2.7+3.4 +pytest-cov>=2.8.1 +pytest-randomly==1.2.3 # needed for python 2.7+3.4 +pytest-aiohttp>=0.3.0 diff --git a/samples/server/petstore/python-aiohttp-srclayout/tests/__init__.py b/samples/server/petstore/python-aiohttp-srclayout/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/samples/server/petstore/python-aiohttp-srclayout/tests/conftest.py b/samples/server/petstore/python-aiohttp-srclayout/tests/conftest.py new file mode 100644 index 000000000000..182760e678bd --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/tests/conftest.py @@ -0,0 +1,21 @@ +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', pythonic_params=True, + pass_context_arg_name='request') + return loop.run_until_complete(aiohttp_client(app.app)) diff --git a/samples/server/petstore/python-aiohttp-srclayout/tests/test_pet_controller.py b/samples/server/petstore/python-aiohttp-srclayout/tests/test_pet_controller.py new file mode 100644 index 000000000000..5f6384a34f67 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/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-srclayout/tests/test_store_controller.py b/samples/server/petstore/python-aiohttp-srclayout/tests/test_store_controller.py new file mode 100644 index 000000000000..9d376a5f7a5b --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/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-srclayout/tests/test_user_controller.py b/samples/server/petstore/python-aiohttp-srclayout/tests/test_user_controller.py new file mode 100644 index 000000000000..6564329315ed --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/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') + diff --git a/samples/server/petstore/python-aiohttp-srclayout/tox.ini b/samples/server/petstore/python-aiohttp-srclayout/tox.ini new file mode 100644 index 000000000000..6fd712372aea --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/tox.ini @@ -0,0 +1,10 @@ +[tox] +envlist = py3 +skipsdist=True + +[testenv] +deps=-r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +commands= + pytest --cov=src/openapi_server \ No newline at end of file From 0bbd2b7c085d33a5b7fa8d621cac074400db5a69 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 3 Apr 2020 18:26:32 -0500 Subject: [PATCH 06/20] [Python] copy test files preserving history --- .../{python-aiohttp => python-aiohttp-srclayout}/Makefile | 0 .../dev-requirements.txt | 0 .../petstore/{python-aiohttp => python-aiohttp-srclayout}/pom.xml | 0 .../{python-aiohttp => python-aiohttp-srclayout}/test_python3.sh | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename samples/server/petstore/{python-aiohttp => python-aiohttp-srclayout}/Makefile (100%) rename samples/server/petstore/{python-aiohttp => python-aiohttp-srclayout}/dev-requirements.txt (100%) rename samples/server/petstore/{python-aiohttp => python-aiohttp-srclayout}/pom.xml (100%) rename samples/server/petstore/{python-aiohttp => python-aiohttp-srclayout}/test_python3.sh (100%) diff --git a/samples/server/petstore/python-aiohttp/Makefile b/samples/server/petstore/python-aiohttp-srclayout/Makefile similarity index 100% rename from samples/server/petstore/python-aiohttp/Makefile rename to samples/server/petstore/python-aiohttp-srclayout/Makefile diff --git a/samples/server/petstore/python-aiohttp/dev-requirements.txt b/samples/server/petstore/python-aiohttp-srclayout/dev-requirements.txt similarity index 100% rename from samples/server/petstore/python-aiohttp/dev-requirements.txt rename to samples/server/petstore/python-aiohttp-srclayout/dev-requirements.txt diff --git a/samples/server/petstore/python-aiohttp/pom.xml b/samples/server/petstore/python-aiohttp-srclayout/pom.xml similarity index 100% rename from samples/server/petstore/python-aiohttp/pom.xml rename to samples/server/petstore/python-aiohttp-srclayout/pom.xml diff --git a/samples/server/petstore/python-aiohttp/test_python3.sh b/samples/server/petstore/python-aiohttp-srclayout/test_python3.sh similarity index 100% rename from samples/server/petstore/python-aiohttp/test_python3.sh rename to samples/server/petstore/python-aiohttp-srclayout/test_python3.sh From 23a64a7baa745ccafb0a1b3ae901852c05868c03 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 3 Apr 2020 18:40:21 -0500 Subject: [PATCH 07/20] [Python] Make a conflict to preserve file copy history --- .../petstore/python-aiohttp/{Makefile => Makefile.make_conflict} | 0 .../{dev-requirements.txt => dev-requirements.txt.make_conflict} | 0 .../petstore/python-aiohttp/{pom.xml => pom.xml.make_conflict} | 0 .../{test_python3.sh => test_python3.sh.make_conflict} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename samples/server/petstore/python-aiohttp/{Makefile => Makefile.make_conflict} (100%) rename samples/server/petstore/python-aiohttp/{dev-requirements.txt => dev-requirements.txt.make_conflict} (100%) rename samples/server/petstore/python-aiohttp/{pom.xml => pom.xml.make_conflict} (100%) rename samples/server/petstore/python-aiohttp/{test_python3.sh => test_python3.sh.make_conflict} (100%) diff --git a/samples/server/petstore/python-aiohttp/Makefile b/samples/server/petstore/python-aiohttp/Makefile.make_conflict similarity index 100% rename from samples/server/petstore/python-aiohttp/Makefile rename to samples/server/petstore/python-aiohttp/Makefile.make_conflict diff --git a/samples/server/petstore/python-aiohttp/dev-requirements.txt b/samples/server/petstore/python-aiohttp/dev-requirements.txt.make_conflict similarity index 100% rename from samples/server/petstore/python-aiohttp/dev-requirements.txt rename to samples/server/petstore/python-aiohttp/dev-requirements.txt.make_conflict diff --git a/samples/server/petstore/python-aiohttp/pom.xml b/samples/server/petstore/python-aiohttp/pom.xml.make_conflict similarity index 100% rename from samples/server/petstore/python-aiohttp/pom.xml rename to samples/server/petstore/python-aiohttp/pom.xml.make_conflict diff --git a/samples/server/petstore/python-aiohttp/test_python3.sh b/samples/server/petstore/python-aiohttp/test_python3.sh.make_conflict similarity index 100% rename from samples/server/petstore/python-aiohttp/test_python3.sh rename to samples/server/petstore/python-aiohttp/test_python3.sh.make_conflict From a649ac450efc728a9d5c78af384e084db6a1c031 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 3 Apr 2020 18:49:34 -0500 Subject: [PATCH 08/20] [Python] customize pom.xml for src layout tests --- samples/server/petstore/python-aiohttp-srclayout/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/server/petstore/python-aiohttp-srclayout/pom.xml b/samples/server/petstore/python-aiohttp-srclayout/pom.xml index 26c4b7c492d1..2dc0adaf279c 100644 --- a/samples/server/petstore/python-aiohttp-srclayout/pom.xml +++ b/samples/server/petstore/python-aiohttp-srclayout/pom.xml @@ -1,10 +1,10 @@ 4.0.0 org.openapitools - PythonAiohttpServer + PythonAiohttpSrcLayoutServer pom 1.0-SNAPSHOT - Python Aiohttp Server + Python Aiohttp Server (/src layout) From ef999c283c19fb192a01a98c6a16222329a848cd Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 3 Apr 2020 19:01:25 -0500 Subject: [PATCH 09/20] [Python] add python-aiohttp-srclayout tests --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index b88e81362edc..5de52ee9deea 100644 --- a/pom.xml +++ b/pom.xml @@ -1232,6 +1232,7 @@ + samples/server/petstore/python-aiohttp-srclayout samples/client/petstore/bash samples/client/petstore/c From b03adefccfcfe39db754528ad2316dcefdff62da Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 3 Apr 2020 20:08:04 -0500 Subject: [PATCH 10/20] [Python] Fix server tests by updating requirements Reverts the PR that disabled python2 server tests: https://github.com/OpenAPITools/openapi-generator/pull/4949 Reverts commits that disabled python3 server tests: 9adfedbfbb45c7059e64b3d9a285710bded4fb62 17ee990baaa80585242c7a07d64e2be4888fcfd0 Issue about the python 3 tests: https://github.com/OpenAPITools/openapi-generator/issues/5235 I couldn't find an issue about the python2 tests being disabled. I'm guessing build errors like the following were the trigger: https://travis-ci.org/github/OpenAPITools/openapi-generator/builds/634238181 Let's see what breaks! --- .../main/resources/python-aiohttp/requirements.mustache | 6 +++--- .../src/main/resources/python-flask/requirements.mustache | 8 ++++++++ pom.xml | 8 +++----- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache index 835c75de58a6..15a75d1984ca 100644 --- a/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache @@ -1,3 +1,3 @@ -connexion[aiohttp,swagger-ui] == 2.0.2 -swagger-ui-bundle == 0.0.2 -aiohttp_jinja2 == 1.1.0 +connexion[aiohttp,swagger-ui] == 2.6.0 +swagger-ui-bundle == 0.0.6 +aiohttp_jinja2 == 1.2.0 diff --git a/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache b/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache index b5dab7b9af0b..f870ae9b417e 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache @@ -1,7 +1,15 @@ connexion >= 2.6.0; python_version>="3.6" +# connexion 2.6 dropped support for python < 3.6 connexion >= 2.3.0; python_version=="3.5" connexion >= 2.3.0; python_version=="3.4" +{{#supportPython2}} connexion == 2.4.0; python_version<="2.7" +{{/supportPython2}} +# werkzeug is a dep of connexion. They need to record their dep. +# https://github.com/zalando/connexion/issues/1149 +# https://github.com/zalando/connexion/pull/1044 +werkzeug == 0.16.1; python_version<"3.6" +werkzeug >= 1.0.0; python_version>="3.6" swagger-ui-bundle >= 0.0.2 python_dateutil >= 2.6.0 {{#supportPython2}} diff --git a/pom.xml b/pom.xml index 5de52ee9deea..10d635505bbd 100644 --- a/pom.xml +++ b/pom.xml @@ -1230,8 +1230,7 @@ - + samples/server/petstore/python-aiohttp samples/server/petstore/python-aiohttp-srclayout samples/client/petstore/bash @@ -1275,9 +1274,8 @@ samples/client/petstore/typescript-angular-v6-provided-in-root samples/client/petstore/typescript-angular-v7-provided-in-root samples/server/petstore/rust-server - - + samples/server/petstore/python-flask + samples/server/petstore/python-flask-python2 From 458de3f923987c4fdf28af6b04fc07188e8a6075 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 3 Apr 2020 21:38:41 -0500 Subject: [PATCH 11/20] [Python] Copy setup.py to python-aiohttp --- .../resources/{python-flask => python-aiohttp}/setup.mustache | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/openapi-generator/src/main/resources/{python-flask => python-aiohttp}/setup.mustache (100%) diff --git a/modules/openapi-generator/src/main/resources/python-flask/setup.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/setup.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/python-flask/setup.mustache rename to modules/openapi-generator/src/main/resources/python-aiohttp/setup.mustache From c76879714208f0762af1384f1c113270a848e92b Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 3 Apr 2020 21:37:27 -0500 Subject: [PATCH 12/20] [Python] Save history while copying setup.py to python-aiohttp --- .../python-flask/{setup.mustache => setup.mustache.save_history} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/openapi-generator/src/main/resources/python-flask/{setup.mustache => setup.mustache.save_history} (100%) diff --git a/modules/openapi-generator/src/main/resources/python-flask/setup.mustache b/modules/openapi-generator/src/main/resources/python-flask/setup.mustache.save_history similarity index 100% rename from modules/openapi-generator/src/main/resources/python-flask/setup.mustache rename to modules/openapi-generator/src/main/resources/python-flask/setup.mustache.save_history From b791fff77eb0e688170b611cc23af08677e7de77 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 3 Apr 2020 22:12:17 -0500 Subject: [PATCH 13/20] [Python] Add aiohttp server setup.py --- .../languages/PythonAiohttpConnexionServerCodegen.java | 1 + .../src/main/resources/python-aiohttp/setup.mustache | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) 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 index 7f9dd74bf3d2..1e29687eb8cc 100644 --- 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 @@ -71,6 +71,7 @@ protected void addSupportingFiles() { supportingFiles.add(new SupportingFile("conftest.mustache", testPackage, "conftest.py")); supportingFiles.add(new SupportingFile("__init__test.mustache", testPackage, "__init__.py")); supportingFiles.add(new SupportingFile("__init__main.mustache", packagePath(), "__init__.py")); + supportingFiles.add(new SupportingFile("setup.mustache", "", "setup.py")); supportingFiles.add(new SupportingFile("tox.mustache", "", "tox.ini")); supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); } diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/setup.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/setup.mustache index 4d562b787356..638e78905309 100644 --- a/modules/openapi-generator/src/main/resources/python-aiohttp/setup.mustache +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/setup.mustache @@ -14,10 +14,9 @@ VERSION = "{{packageVersion}}" # http://pypi.python.org/pypi/setuptools REQUIRES = [ - "connexion>=2.0.2", - "swagger-ui-bundle>=0.0.2", - "python_dateutil>=2.6.0"{{#supportPython2}}, - "typing>=3.5.2.2"{{/supportPython2}} + "connexion==2.6.0", + "swagger-ui-bundle==0.0.6", + "aiohttp_jinja2==1.2.0", ] setup( From 8a63a5cf2a6a7d022385bc60072340af4dfaafaf Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 3 Apr 2020 22:17:12 -0500 Subject: [PATCH 14/20] [Python] Fix python server tests with src layout --- .../src/main/resources/python-aiohttp/conftest.mustache | 3 ++- .../src/main/resources/python-aiohttp/setup.mustache | 2 +- .../src/main/resources/python-aiohttp/tox.mustache | 3 ++- .../src/main/resources/python-flask/setup.mustache | 2 +- .../src/main/resources/python-flask/tox.mustache | 3 ++- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/conftest.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/conftest.mustache index 5b73ee7f5118..a48d0bec6217 100644 --- a/modules/openapi-generator/src/main/resources/python-aiohttp/conftest.mustache +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/conftest.mustache @@ -11,7 +11,8 @@ def client(loop, aiohttp_client): options = { "swagger_ui": True } - specification_dir = os.path.join(os.path.dirname(__file__), '..', + specification_dir = os.path.join(os.path.dirname(__file__), '..',{{#pythonSrcRoot}} + "{{{.}}}",{{/pythonSrcRoot}} '{{packageName}}', 'openapi') app = connexion.AioHttpApp(__name__, specification_dir=specification_dir, diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/setup.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/setup.mustache index 638e78905309..be170fa84312 100644 --- a/modules/openapi-generator/src/main/resources/python-aiohttp/setup.mustache +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/setup.mustache @@ -29,7 +29,7 @@ setup( install_requires=REQUIRES, packages=find_packages({{#pythonSrcRoot}}"{{{.}}}"{{/pythonSrcRoot}}),{{#pythonSrcRoot}} package_dir={"": "{{{.}}}"},{{/pythonSrcRoot}} - package_data={'': ['openapi/openapi.yaml']}, + package_data={'': ['{{#pythonSrcRoot}}{{{.}}}/{{/pythonSrcRoot}}openapi/openapi.yaml']}, include_package_data=True, entry_points={ 'console_scripts': ['{{packageName}}={{packageName}}.__main__:main']}, diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/tox.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/tox.mustache index 852319368691..76e368c23931 100644 --- a/modules/openapi-generator/src/main/resources/python-aiohttp/tox.mustache +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/tox.mustache @@ -5,6 +5,7 @@ skipsdist=True [testenv] deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt + {toxinidir} commands= - {{^useNose}}pytest --cov={{{pythonSrcRoot}}}{{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}} \ No newline at end of file + {{^useNose}}pytest --cov={{{pythonSrcRoot}}}{{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}} diff --git a/modules/openapi-generator/src/main/resources/python-flask/setup.mustache b/modules/openapi-generator/src/main/resources/python-flask/setup.mustache index 4d562b787356..d6528d865631 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/setup.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/setup.mustache @@ -30,7 +30,7 @@ setup( install_requires=REQUIRES, packages=find_packages({{#pythonSrcRoot}}"{{{.}}}"{{/pythonSrcRoot}}),{{#pythonSrcRoot}} package_dir={"": "{{{.}}}"},{{/pythonSrcRoot}} - package_data={'': ['openapi/openapi.yaml']}, + package_data={'': ['{{#pythonSrcRoot}}{{{.}}}/{{/pythonSrcRoot}}openapi/openapi.yaml']}, include_package_data=True, entry_points={ 'console_scripts': ['{{packageName}}={{packageName}}.__main__:main']}, diff --git a/modules/openapi-generator/src/main/resources/python-flask/tox.mustache b/modules/openapi-generator/src/main/resources/python-flask/tox.mustache index 95a527aafb6e..4caf6a1a3214 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/tox.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/tox.mustache @@ -4,6 +4,7 @@ envlist = {{#supportPython2}}py27, {{/supportPython2}}py3 [testenv] deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt + {toxinidir} commands= - {{^useNose}}pytest --cov={{{pythonSrcRoot}}}{{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}} \ No newline at end of file + {{^useNose}}pytest --cov={{{pythonSrcRoot}}}{{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}} From 03988270cc9e0879757da97f66a184c825efed76 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 3 Apr 2020 22:34:35 -0500 Subject: [PATCH 15/20] [Python] bump Flask-Testing version --- .../src/main/resources/python-flask/test-requirements.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/python-flask/test-requirements.mustache b/modules/openapi-generator/src/main/resources/python-flask/test-requirements.mustache index efad5423cb5e..7157c73aa34b 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/test-requirements.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/test-requirements.mustache @@ -10,4 +10,4 @@ pytest~=4.6.7 # needed for python 2.7+3.4 pytest-cov>=2.8.1 pytest-randomly==1.2.3 # needed for python 2.7+3.4 {{/useNose}} -flask_testing==0.6.1 \ No newline at end of file +Flask-Testing==0.8.0 From 8dbc5f6b342f6d4e1c3ab5545473a21d2a25d734 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Sat, 4 Apr 2020 00:45:33 -0500 Subject: [PATCH 16/20] [Python] Pin pyyaml for py2.7 flask server --- .../src/main/resources/python-flask/requirements.mustache | 4 ++++ .../src/main/resources/python-flask/tox.mustache | 1 + 2 files changed, 5 insertions(+) diff --git a/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache b/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache index f870ae9b417e..b22d24a3eb36 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache @@ -14,5 +14,9 @@ swagger-ui-bundle >= 0.0.2 python_dateutil >= 2.6.0 {{#supportPython2}} typing >= 3.5.2.2 +# For specs with timestamps, pyyaml 5.3 broke connexion's spec parsing in python 2. +# Connexion uses copy.deepcopy() on the spec, thus hitting this bug: +# https://github.com/yaml/pyyaml/issues/387 +pyyaml < 5.3; python_version<="2.7" {{/supportPython2}} setuptools >= 21.0.0 diff --git a/modules/openapi-generator/src/main/resources/python-flask/tox.mustache b/modules/openapi-generator/src/main/resources/python-flask/tox.mustache index 4caf6a1a3214..d342cd5f573c 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/tox.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/tox.mustache @@ -1,5 +1,6 @@ [tox] envlist = {{#supportPython2}}py27, {{/supportPython2}}py3 +skipsdist=True [testenv] deps=-r{toxinidir}/requirements.txt From ef445968526d73f72038a62e66848947e7842c5b Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Sat, 4 Apr 2020 02:27:11 -0500 Subject: [PATCH 17/20] [Python] simplify flask server requirements --- .../main/resources/python-flask/requirements.mustache | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache b/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache index b22d24a3eb36..84a506978b99 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache @@ -1,15 +1,13 @@ connexion >= 2.6.0; python_version>="3.6" # connexion 2.6 dropped support for python < 3.6 -connexion >= 2.3.0; python_version=="3.5" -connexion >= 2.3.0; python_version=="3.4" +connexion >= 2.3.0; python_version=="3.5" or python_version=="3.4" {{#supportPython2}} connexion == 2.4.0; python_version<="2.7" {{/supportPython2}} -# werkzeug is a dep of connexion. They need to record their dep. -# https://github.com/zalando/connexion/issues/1149 +# connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug +# we must peg werkzeug versions below to fix connexion # https://github.com/zalando/connexion/pull/1044 -werkzeug == 0.16.1; python_version<"3.6" -werkzeug >= 1.0.0; python_version>="3.6" +werkzeug == 0.16.1; python_version=="3.5" or python_version=="3.4" swagger-ui-bundle >= 0.0.2 python_dateutil >= 2.6.0 {{#supportPython2}} From 743a618e81c7a39a43e3225f0ac16277d0ca7c9e Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Sat, 4 Apr 2020 00:49:35 -0500 Subject: [PATCH 18/20] consolidate server tests --- pom.xml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 10d635505bbd..86275e1e797f 100644 --- a/pom.xml +++ b/pom.xml @@ -1230,8 +1230,14 @@ + samples/server/petstore/python-aiohttp samples/server/petstore/python-aiohttp-srclayout + samples/server/petstore/python-flask + samples/server/petstore/python-flask-python2 + samples/server/petstore/php-slim + samples/server/petstore/php-slim4 + samples/server/petstore/rust-server samples/client/petstore/bash samples/client/petstore/c @@ -1241,8 +1247,6 @@ samples/client/petstore/php/OpenAPIClient-php samples/openapi3/client/petstore/php/OpenAPIClient-php - samples/server/petstore/php-slim - samples/server/petstore/php-slim4 samples/client/petstore/javascript samples/client/petstore/javascript-es6 @@ -1273,9 +1277,6 @@ samples/client/petstore/typescript-angular-v4.3/npm--> samples/client/petstore/typescript-angular-v6-provided-in-root samples/client/petstore/typescript-angular-v7-provided-in-root - samples/server/petstore/rust-server - samples/server/petstore/python-flask - samples/server/petstore/python-flask-python2 From 2f5148bc753a0e2bcee14d29715185cee0351fc4 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Sat, 4 Apr 2020 00:54:39 -0500 Subject: [PATCH 19/20] [Python] rebuild python server samples --- .../.openapi-generator/VERSION | 2 +- .../python-aiohttp-srclayout/requirements.txt | 6 +-- .../python-aiohttp-srclayout/setup.py | 40 +++++++++++++++++++ .../tests/conftest.py | 1 + .../petstore/python-aiohttp-srclayout/tox.ini | 3 +- .../petstore/python-aiohttp/requirements.txt | 6 +-- .../server/petstore/python-aiohttp/setup.py | 39 ++++++++++++++++++ .../server/petstore/python-aiohttp/tox.ini | 3 +- .../python-flask-python2/requirements.txt | 12 +++++- .../test-requirements.txt | 2 +- .../petstore/python-flask-python2/tox.ini | 4 +- .../petstore/python-flask/requirements.txt | 9 +++-- .../python-flask/test-requirements.txt | 2 +- samples/server/petstore/python-flask/tox.ini | 4 +- 14 files changed, 115 insertions(+), 18 deletions(-) create mode 100644 samples/server/petstore/python-aiohttp-srclayout/setup.py create mode 100644 samples/server/petstore/python-aiohttp/setup.py diff --git a/samples/server/petstore/python-aiohttp-srclayout/.openapi-generator/VERSION b/samples/server/petstore/python-aiohttp-srclayout/.openapi-generator/VERSION index bfbf77eb7fad..b5d898602c2c 100644 --- a/samples/server/petstore/python-aiohttp-srclayout/.openapi-generator/VERSION +++ b/samples/server/petstore/python-aiohttp-srclayout/.openapi-generator/VERSION @@ -1 +1 @@ -4.3.0-SNAPSHOT \ No newline at end of file +4.3.1-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/python-aiohttp-srclayout/requirements.txt b/samples/server/petstore/python-aiohttp-srclayout/requirements.txt index 835c75de58a6..15a75d1984ca 100644 --- a/samples/server/petstore/python-aiohttp-srclayout/requirements.txt +++ b/samples/server/petstore/python-aiohttp-srclayout/requirements.txt @@ -1,3 +1,3 @@ -connexion[aiohttp,swagger-ui] == 2.0.2 -swagger-ui-bundle == 0.0.2 -aiohttp_jinja2 == 1.1.0 +connexion[aiohttp,swagger-ui] == 2.6.0 +swagger-ui-bundle == 0.0.6 +aiohttp_jinja2 == 1.2.0 diff --git a/samples/server/petstore/python-aiohttp-srclayout/setup.py b/samples/server/petstore/python-aiohttp-srclayout/setup.py new file mode 100644 index 000000000000..6f62aec44149 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/setup.py @@ -0,0 +1,40 @@ +# coding: utf-8 + +import sys +from setuptools import setup, find_packages + +NAME = "openapi_server" +VERSION = "1.0.0" + +# To install the library, run the following +# +# python setup.py install +# +# prerequisite: setuptools +# http://pypi.python.org/pypi/setuptools + +REQUIRES = [ + "connexion==2.6.0", + "swagger-ui-bundle==0.0.6", + "aiohttp_jinja2==1.2.0", +] + +setup( + name=NAME, + version=VERSION, + description="OpenAPI Petstore", + author_email="", + url="", + keywords=["OpenAPI", "OpenAPI Petstore"], + install_requires=REQUIRES, + packages=find_packages("src/"), + package_dir={"": "src/"}, + package_data={'': ['src//openapi/openapi.yaml']}, + include_package_data=True, + entry_points={ + 'console_scripts': ['openapi_server=openapi_server.__main__:main']}, + long_description="""\ + This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + """ +) + diff --git a/samples/server/petstore/python-aiohttp-srclayout/tests/conftest.py b/samples/server/petstore/python-aiohttp-srclayout/tests/conftest.py index 182760e678bd..578bf18e982c 100644 --- a/samples/server/petstore/python-aiohttp-srclayout/tests/conftest.py +++ b/samples/server/petstore/python-aiohttp-srclayout/tests/conftest.py @@ -12,6 +12,7 @@ def client(loop, aiohttp_client): "swagger_ui": True } specification_dir = os.path.join(os.path.dirname(__file__), '..', + "src/", 'openapi_server', 'openapi') app = connexion.AioHttpApp(__name__, specification_dir=specification_dir, diff --git a/samples/server/petstore/python-aiohttp-srclayout/tox.ini b/samples/server/petstore/python-aiohttp-srclayout/tox.ini index 6fd712372aea..25d12bb84c0b 100644 --- a/samples/server/petstore/python-aiohttp-srclayout/tox.ini +++ b/samples/server/petstore/python-aiohttp-srclayout/tox.ini @@ -5,6 +5,7 @@ skipsdist=True [testenv] deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt + {toxinidir} commands= - pytest --cov=src/openapi_server \ No newline at end of file + pytest --cov=src/openapi_server diff --git a/samples/server/petstore/python-aiohttp/requirements.txt b/samples/server/petstore/python-aiohttp/requirements.txt index 835c75de58a6..15a75d1984ca 100644 --- a/samples/server/petstore/python-aiohttp/requirements.txt +++ b/samples/server/petstore/python-aiohttp/requirements.txt @@ -1,3 +1,3 @@ -connexion[aiohttp,swagger-ui] == 2.0.2 -swagger-ui-bundle == 0.0.2 -aiohttp_jinja2 == 1.1.0 +connexion[aiohttp,swagger-ui] == 2.6.0 +swagger-ui-bundle == 0.0.6 +aiohttp_jinja2 == 1.2.0 diff --git a/samples/server/petstore/python-aiohttp/setup.py b/samples/server/petstore/python-aiohttp/setup.py new file mode 100644 index 000000000000..b6a25d8966e5 --- /dev/null +++ b/samples/server/petstore/python-aiohttp/setup.py @@ -0,0 +1,39 @@ +# coding: utf-8 + +import sys +from setuptools import setup, find_packages + +NAME = "openapi_server" +VERSION = "1.0.0" + +# To install the library, run the following +# +# python setup.py install +# +# prerequisite: setuptools +# http://pypi.python.org/pypi/setuptools + +REQUIRES = [ + "connexion==2.6.0", + "swagger-ui-bundle==0.0.6", + "aiohttp_jinja2==1.2.0", +] + +setup( + name=NAME, + version=VERSION, + description="OpenAPI Petstore", + author_email="", + url="", + keywords=["OpenAPI", "OpenAPI Petstore"], + install_requires=REQUIRES, + packages=find_packages(), + package_data={'': ['openapi/openapi.yaml']}, + include_package_data=True, + entry_points={ + 'console_scripts': ['openapi_server=openapi_server.__main__:main']}, + long_description="""\ + This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + """ +) + diff --git a/samples/server/petstore/python-aiohttp/tox.ini b/samples/server/petstore/python-aiohttp/tox.ini index 0f7cd42b8a8b..f66b2d84cdaa 100644 --- a/samples/server/petstore/python-aiohttp/tox.ini +++ b/samples/server/petstore/python-aiohttp/tox.ini @@ -5,6 +5,7 @@ skipsdist=True [testenv] deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt + {toxinidir} commands= - pytest --cov=openapi_server \ No newline at end of file + pytest --cov=openapi_server diff --git a/samples/server/petstore/python-flask-python2/requirements.txt b/samples/server/petstore/python-flask-python2/requirements.txt index 47350085aa10..5adbaa8d5d8e 100644 --- a/samples/server/petstore/python-flask-python2/requirements.txt +++ b/samples/server/petstore/python-flask-python2/requirements.txt @@ -1,8 +1,16 @@ connexion >= 2.6.0; python_version>="3.6" -connexion >= 2.3.0; python_version=="3.5" -connexion >= 2.3.0; python_version=="3.4" +# connexion 2.6 dropped support for python < 3.6 +connexion >= 2.3.0; python_version=="3.5" or python_version=="3.4" connexion == 2.4.0; python_version<="2.7" +# connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug +# we must peg werkzeug versions below to fix connexion +# https://github.com/zalando/connexion/pull/1044 +werkzeug == 0.16.1; python_version=="3.5" or python_version=="3.4" swagger-ui-bundle >= 0.0.2 python_dateutil >= 2.6.0 typing >= 3.5.2.2 +# For specs with timestamps, pyyaml 5.3 broke connexion's spec parsing in python 2. +# Connexion uses copy.deepcopy() on the spec, thus hitting this bug: +# https://github.com/yaml/pyyaml/issues/387 +pyyaml < 5.3; python_version<="2.7" setuptools >= 21.0.0 diff --git a/samples/server/petstore/python-flask-python2/test-requirements.txt b/samples/server/petstore/python-flask-python2/test-requirements.txt index a2626d875ff4..0970f28c7c5a 100644 --- a/samples/server/petstore/python-flask-python2/test-requirements.txt +++ b/samples/server/petstore/python-flask-python2/test-requirements.txt @@ -1,4 +1,4 @@ pytest~=4.6.7 # needed for python 2.7+3.4 pytest-cov>=2.8.1 pytest-randomly==1.2.3 # needed for python 2.7+3.4 -flask_testing==0.6.1 \ No newline at end of file +Flask-Testing==0.8.0 diff --git a/samples/server/petstore/python-flask-python2/tox.ini b/samples/server/petstore/python-flask-python2/tox.ini index d05c607610ce..d63c4ac5aac0 100644 --- a/samples/server/petstore/python-flask-python2/tox.ini +++ b/samples/server/petstore/python-flask-python2/tox.ini @@ -1,9 +1,11 @@ [tox] envlist = py27, py3 +skipsdist=True [testenv] deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt + {toxinidir} commands= - pytest --cov=openapi_server \ No newline at end of file + pytest --cov=openapi_server diff --git a/samples/server/petstore/python-flask/requirements.txt b/samples/server/petstore/python-flask/requirements.txt index 2639eedf1361..ff88954e78b6 100644 --- a/samples/server/petstore/python-flask/requirements.txt +++ b/samples/server/petstore/python-flask/requirements.txt @@ -1,7 +1,10 @@ connexion >= 2.6.0; python_version>="3.6" -connexion >= 2.3.0; python_version=="3.5" -connexion >= 2.3.0; python_version=="3.4" -connexion == 2.4.0; python_version<="2.7" +# connexion 2.6 dropped support for python < 3.6 +connexion >= 2.3.0; python_version=="3.5" or python_version=="3.4" +# connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug +# we must peg werkzeug versions below to fix connexion +# https://github.com/zalando/connexion/pull/1044 +werkzeug == 0.16.1; python_version=="3.5" or python_version=="3.4" swagger-ui-bundle >= 0.0.2 python_dateutil >= 2.6.0 setuptools >= 21.0.0 diff --git a/samples/server/petstore/python-flask/test-requirements.txt b/samples/server/petstore/python-flask/test-requirements.txt index a2626d875ff4..0970f28c7c5a 100644 --- a/samples/server/petstore/python-flask/test-requirements.txt +++ b/samples/server/petstore/python-flask/test-requirements.txt @@ -1,4 +1,4 @@ pytest~=4.6.7 # needed for python 2.7+3.4 pytest-cov>=2.8.1 pytest-randomly==1.2.3 # needed for python 2.7+3.4 -flask_testing==0.6.1 \ No newline at end of file +Flask-Testing==0.8.0 diff --git a/samples/server/petstore/python-flask/tox.ini b/samples/server/petstore/python-flask/tox.ini index cff71191e6cb..f66b2d84cdaa 100644 --- a/samples/server/petstore/python-flask/tox.ini +++ b/samples/server/petstore/python-flask/tox.ini @@ -1,9 +1,11 @@ [tox] envlist = py3 +skipsdist=True [testenv] deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt + {toxinidir} commands= - pytest --cov=openapi_server \ No newline at end of file + pytest --cov=openapi_server From 85665f1f07d89cb868a2372946d4387ee50c7538 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Tue, 7 Apr 2020 02:05:38 -0500 Subject: [PATCH 20/20] [Python] Fix python server requirements for older pythons Documented minimum python version for our aiohttp server is 3.5. Documented minimum python version for our flask server is 3.4. Connexion 2.3 is the last version to support python 3.4 and 3.5, so fix the version pinning to correctly select <=2.3 for these EOL python versions. Newer pythons should get the latest if possible as there many relevant bug fixes. Werkzeug also needs to be pinned for these old versions in the aiohttp server just like for the flask server. 3.4 and 3.5 are EOL. We really should increase the minimum supported version, but that is for another PR to do. --- .../main/resources/python-aiohttp/requirements.mustache | 8 +++++++- .../src/main/resources/python-flask/requirements.mustache | 8 ++++---- .../petstore/python-aiohttp-srclayout/requirements.txt | 8 +++++++- samples/server/petstore/python-aiohttp/requirements.txt | 8 +++++++- .../server/petstore/python-flask-python2/requirements.txt | 8 ++++---- samples/server/petstore/python-flask/requirements.txt | 6 +++--- 6 files changed, 32 insertions(+), 14 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache index 15a75d1984ca..e0dd796ca9fb 100644 --- a/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache @@ -1,3 +1,9 @@ -connexion[aiohttp,swagger-ui] == 2.6.0 +connexion[aiohttp,swagger-ui] >= 2.6.0; python_version>="3.6" +# 2.3 is the last version that supports python 3.5 +connexion[aiohttp,swagger-ui] <= 2.3.0; python_version=="3.5" or python_version=="3.4" +# connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug +# we must peg werkzeug versions below to fix connexion +# https://github.com/zalando/connexion/pull/1044 +werkzeug == 0.16.1; python_version=="3.5" or python_version=="3.4" swagger-ui-bundle == 0.0.6 aiohttp_jinja2 == 1.2.0 diff --git a/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache b/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache index 84a506978b99..6e3f7d524c2c 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache @@ -1,8 +1,8 @@ -connexion >= 2.6.0; python_version>="3.6" -# connexion 2.6 dropped support for python < 3.6 -connexion >= 2.3.0; python_version=="3.5" or python_version=="3.4" +connexion[swagger-ui] >= 2.6.0; python_version>="3.6" +# 2.3 is the last version that supports python 3.4-3.5 +connexion[swagger-ui] <= 2.3.0; python_version=="3.5" or python_version=="3.4" {{#supportPython2}} -connexion == 2.4.0; python_version<="2.7" +connexion[swagger-ui] == 2.4.0; python_version<="2.7" {{/supportPython2}} # connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug # we must peg werkzeug versions below to fix connexion diff --git a/samples/server/petstore/python-aiohttp-srclayout/requirements.txt b/samples/server/petstore/python-aiohttp-srclayout/requirements.txt index 15a75d1984ca..e0dd796ca9fb 100644 --- a/samples/server/petstore/python-aiohttp-srclayout/requirements.txt +++ b/samples/server/petstore/python-aiohttp-srclayout/requirements.txt @@ -1,3 +1,9 @@ -connexion[aiohttp,swagger-ui] == 2.6.0 +connexion[aiohttp,swagger-ui] >= 2.6.0; python_version>="3.6" +# 2.3 is the last version that supports python 3.5 +connexion[aiohttp,swagger-ui] <= 2.3.0; python_version=="3.5" or python_version=="3.4" +# connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug +# we must peg werkzeug versions below to fix connexion +# https://github.com/zalando/connexion/pull/1044 +werkzeug == 0.16.1; python_version=="3.5" or python_version=="3.4" swagger-ui-bundle == 0.0.6 aiohttp_jinja2 == 1.2.0 diff --git a/samples/server/petstore/python-aiohttp/requirements.txt b/samples/server/petstore/python-aiohttp/requirements.txt index 15a75d1984ca..e0dd796ca9fb 100644 --- a/samples/server/petstore/python-aiohttp/requirements.txt +++ b/samples/server/petstore/python-aiohttp/requirements.txt @@ -1,3 +1,9 @@ -connexion[aiohttp,swagger-ui] == 2.6.0 +connexion[aiohttp,swagger-ui] >= 2.6.0; python_version>="3.6" +# 2.3 is the last version that supports python 3.5 +connexion[aiohttp,swagger-ui] <= 2.3.0; python_version=="3.5" or python_version=="3.4" +# connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug +# we must peg werkzeug versions below to fix connexion +# https://github.com/zalando/connexion/pull/1044 +werkzeug == 0.16.1; python_version=="3.5" or python_version=="3.4" swagger-ui-bundle == 0.0.6 aiohttp_jinja2 == 1.2.0 diff --git a/samples/server/petstore/python-flask-python2/requirements.txt b/samples/server/petstore/python-flask-python2/requirements.txt index 5adbaa8d5d8e..55ef66307fc4 100644 --- a/samples/server/petstore/python-flask-python2/requirements.txt +++ b/samples/server/petstore/python-flask-python2/requirements.txt @@ -1,7 +1,7 @@ -connexion >= 2.6.0; python_version>="3.6" -# connexion 2.6 dropped support for python < 3.6 -connexion >= 2.3.0; python_version=="3.5" or python_version=="3.4" -connexion == 2.4.0; python_version<="2.7" +connexion[swagger-ui] >= 2.6.0; python_version>="3.6" +# 2.3 is the last version that supports python 3.4-3.5 +connexion[swagger-ui] <= 2.3.0; python_version=="3.5" or python_version=="3.4" +connexion[swagger-ui] == 2.4.0; python_version<="2.7" # connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug # we must peg werkzeug versions below to fix connexion # https://github.com/zalando/connexion/pull/1044 diff --git a/samples/server/petstore/python-flask/requirements.txt b/samples/server/petstore/python-flask/requirements.txt index ff88954e78b6..72ed547c4429 100644 --- a/samples/server/petstore/python-flask/requirements.txt +++ b/samples/server/petstore/python-flask/requirements.txt @@ -1,6 +1,6 @@ -connexion >= 2.6.0; python_version>="3.6" -# connexion 2.6 dropped support for python < 3.6 -connexion >= 2.3.0; python_version=="3.5" or python_version=="3.4" +connexion[swagger-ui] >= 2.6.0; python_version>="3.6" +# 2.3 is the last version that supports python 3.4-3.5 +connexion[swagger-ui] <= 2.3.0; python_version=="3.5" or python_version=="3.4" # connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug # we must peg werkzeug versions below to fix connexion # https://github.com/zalando/connexion/pull/1044