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 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| 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 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/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/requirements.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache index 835c75de58a6..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.0.2 -swagger-ui-bundle == 0.0.2 -aiohttp_jinja2 == 1.1.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-aiohttp/setup.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/setup.mustache new file mode 100644 index 000000000000..be170fa84312 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/setup.mustache @@ -0,0 +1,40 @@ +# coding: utf-8 + +import sys +from setuptools import setup, find_packages + +NAME = "{{packageName}}" +VERSION = "{{packageVersion}}" +{{#apiInfo}}{{#apis}}{{^hasMore}} +# 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="{{appName}}", + author_email="{{infoEmail}}", + url="{{packageUrl}}", + keywords=["OpenAPI", "{{appName}}"], + install_requires=REQUIRES, + packages=find_packages({{#pythonSrcRoot}}"{{{.}}}"{{/pythonSrcRoot}}),{{#pythonSrcRoot}} + package_dir={"": "{{{.}}}"},{{/pythonSrcRoot}} + package_data={'': ['{{#pythonSrcRoot}}{{{.}}}/{{/pythonSrcRoot}}openapi/openapi.yaml']}, + include_package_data=True, + entry_points={ + 'console_scripts': ['{{packageName}}={{packageName}}.__main__:main']}, + long_description="""\ + {{appDescription}} + """ +) +{{/hasMore}}{{/apis}}{{/apiInfo}} 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..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={{{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-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/requirements.mustache b/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache index b5dab7b9af0b..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,10 +1,20 @@ -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[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[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 +# 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 {{#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/setup.mustache b/modules/openapi-generator/src/main/resources/python-flask/setup.mustache index bda71175d0da..d6528d865631 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/setup.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/setup.mustache @@ -28,8 +28,9 @@ setup( url="{{packageUrl}}", keywords=["OpenAPI", "{{appName}}"], install_requires=REQUIRES, - packages=find_packages(), - package_data={'': ['openapi/openapi.yaml']}, + packages=find_packages({{#pythonSrcRoot}}"{{{.}}}"{{/pythonSrcRoot}}),{{#pythonSrcRoot}} + package_dir={"": "{{{.}}}"},{{/pythonSrcRoot}} + 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/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 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..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,9 +1,11 @@ [tox] envlist = {{#supportPython2}}py27, {{/supportPython2}}py3 +skipsdist=True [testenv] deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt + {toxinidir} commands= - {{^useNose}}pytest --cov={{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}} \ No newline at end of file + {{^useNose}}pytest --cov={{{pythonSrcRoot}}}{{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}} diff --git a/pom.xml b/pom.xml index b88e81362edc..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,10 +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 - - 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..b5d898602c2c --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/.openapi-generator/VERSION @@ -0,0 +1 @@ +4.3.1-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/python-aiohttp-srclayout/Makefile b/samples/server/petstore/python-aiohttp-srclayout/Makefile new file mode 100644 index 000000000000..763fe2bc4a45 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/Makefile @@ -0,0 +1,18 @@ + #!/bin/bash + +REQUIREMENTS_OUT=requirements.txt.log +VENV=.venv + +clean: + rm -rf $(REQUIREMENTS_OUT) + rm -rf $(VENV) + rm -rf .pytest_cache + rm -rf .coverage + find . -name "*.py[oc]" -delete + find . -name "__pycache__" -delete + +test: clean + bash ./test_python3.sh + +test-all: clean + bash ./test_python3.sh diff --git a/samples/server/petstore/python-aiohttp-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/dev-requirements.txt b/samples/server/petstore/python-aiohttp-srclayout/dev-requirements.txt new file mode 100644 index 000000000000..ccdfca629494 --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/dev-requirements.txt @@ -0,0 +1,2 @@ +tox +flake8 diff --git a/samples/server/petstore/python-aiohttp-srclayout/pom.xml b/samples/server/petstore/python-aiohttp-srclayout/pom.xml new file mode 100644 index 000000000000..2dc0adaf279c --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/pom.xml @@ -0,0 +1,46 @@ + + 4.0.0 + org.openapitools + PythonAiohttpSrcLayoutServer + pom + 1.0-SNAPSHOT + Python Aiohttp Server (/src layout) + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory} + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + test + integration-test + + exec + + + make + + test-all + + + + + + + + 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..e0dd796ca9fb --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/requirements.txt @@ -0,0 +1,9 @@ +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-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/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/test_python3.sh b/samples/server/petstore/python-aiohttp-srclayout/test_python3.sh new file mode 100755 index 000000000000..65d96267d4fb --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/test_python3.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +REQUIREMENTS_FILE=dev-requirements.txt +REQUIREMENTS_OUT=dev-requirements.txt.log +SETUP_OUT=*.egg-info +VENV=.venv +DEACTIVE=false + +export LC_ALL=en_US.UTF-8 +export LANG=en_US.UTF-8 + +### set virtualenv +if [ -z "$VIRTUAL_ENV" ]; then + virtualenv $VENV --no-site-packages --always-copy --python python3 + source $VENV/bin/activate + DEACTIVE=true +fi + +### install dependencies +pip install -r $REQUIREMENTS_FILE | tee -a $REQUIREMENTS_OUT + +### run tests +tox || exit 1 + +### static analysis of code +flake8 --show-source petstore_api/ + +### deactivate virtualenv +if [ $DEACTIVE == true ]; then + deactivate +fi diff --git a/samples/server/petstore/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..578bf18e982c --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/tests/conftest.py @@ -0,0 +1,22 @@ +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__), '..', + "src/", + '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..25d12bb84c0b --- /dev/null +++ b/samples/server/petstore/python-aiohttp-srclayout/tox.ini @@ -0,0 +1,11 @@ +[tox] +envlist = py3 +skipsdist=True + +[testenv] +deps=-r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + {toxinidir} + +commands= + 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..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.0.2 -swagger-ui-bundle == 0.0.2 -aiohttp_jinja2 == 1.1.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/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..55ef66307fc4 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.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 +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..72ed547c4429 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[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 +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