From 061552f5d4ef45f740dbd913096ffa9851354610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20T=C5=AFma?= Date: Fri, 29 Jan 2021 03:54:02 +0100 Subject: [PATCH] [feature][python-flask] Add CORS support to python-flask server (#8472) * Add CORS support to python-flask server generator * Documentation update and CORS support for other generators using the same base class * Trivial sample changes --- docs/generators/python-aiohttp.md | 1 + docs/generators/python-blueplanet.md | 1 + docs/generators/python-flask.md | 1 + .../AbstractPythonConnexionServerCodegen.java | 11 ++++++++++ .../python-aiohttp/__init__main.mustache | 20 ++++++++++++++++++- .../python-aiohttp/requirements.mustache | 3 +++ .../app/requirements.mustache | 4 ++++ .../app/{{packageName}}/__main__.mustache | 9 +++++++++ .../resources/python-flask/__main__.mustache | 9 +++++++++ .../python-flask/requirements.mustache | 4 ++++ .../src/openapi_server/__init__.py | 2 +- .../python-aiohttp/openapi_server/__init__.py | 2 +- .../app/openapi_server/__main__.py | 1 + .../python-flask/openapi_server/__main__.py | 1 + 14 files changed, 66 insertions(+), 3 deletions(-) diff --git a/docs/generators/python-aiohttp.md b/docs/generators/python-aiohttp.md index 1eb75a73aa4..ded84c55242 100644 --- a/docs/generators/python-aiohttp.md +++ b/docs/generators/python-aiohttp.md @@ -12,6 +12,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |defaultController|default controller| |default_controller| |disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|
**false**
The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.
**true**
Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.
|true| |ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true| +|featureCORS|use flask-cors for handling CORS requests| |false| |legacyDiscriminatorBehavior|Set to true for generators with better support for discriminators. (Python, Java, Go, PowerShell, C#have this enabled by default).|
**true**
The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.
**false**
The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.
|true| |packageName|python package name (convention: snake_case).| |openapi_server| |packageVersion|python package version.| |1.0.0| diff --git a/docs/generators/python-blueplanet.md b/docs/generators/python-blueplanet.md index dba4b0a0072..eb4fdab9aa1 100644 --- a/docs/generators/python-blueplanet.md +++ b/docs/generators/python-blueplanet.md @@ -12,6 +12,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |defaultController|default controller| |default_controller| |disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|
**false**
The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.
**true**
Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.
|true| |ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true| +|featureCORS|use flask-cors for handling CORS requests| |false| |legacyDiscriminatorBehavior|Set to true for generators with better support for discriminators. (Python, Java, Go, PowerShell, C#have this enabled by default).|
**true**
The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.
**false**
The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.
|true| |packageName|python package name (convention: snake_case).| |openapi_server| |packageVersion|python package version.| |1.0.0| diff --git a/docs/generators/python-flask.md b/docs/generators/python-flask.md index 4811927404a..a0b17be6482 100644 --- a/docs/generators/python-flask.md +++ b/docs/generators/python-flask.md @@ -12,6 +12,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |defaultController|default controller| |default_controller| |disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|
**false**
The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.
**true**
Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.
|true| |ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true| +|featureCORS|use flask-cors for handling CORS requests| |false| |legacyDiscriminatorBehavior|Set to true for generators with better support for discriminators. (Python, Java, Go, PowerShell, C#have this enabled by default).|
**true**
The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.
**false**
The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.
|true| |packageName|python package name (convention: snake_case).| |openapi_server| |packageVersion|python package version.| |1.0.0| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonConnexionServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonConnexionServerCodegen.java index e1acbc1e166..e9195a684b6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonConnexionServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonConnexionServerCodegen.java @@ -49,6 +49,7 @@ public abstract class AbstractPythonConnexionServerCodegen extends DefaultCodege public static final String CONTROLLER_PACKAGE = "controllerPackage"; public static final String DEFAULT_CONTROLLER = "defaultController"; public static final String SUPPORT_PYTHON2 = "supportPython2"; + public static final String FEATURE_CORS = "featureCORS"; // 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"; @@ -61,6 +62,7 @@ public abstract class AbstractPythonConnexionServerCodegen extends DefaultCodege protected String defaultController; protected Map regexModifiers; protected boolean fixBodyName; + protected boolean featureCORS = Boolean.FALSE; protected boolean useNose = Boolean.FALSE; protected String pythonSrcRoot; @@ -165,6 +167,8 @@ public abstract class AbstractPythonConnexionServerCodegen extends DefaultCodege defaultValue("false")); cliOptions.add(new CliOption("serverPort", "TCP port to listen to in app.run"). defaultValue("8080")); + cliOptions.add(CliOption.newBoolean(FEATURE_CORS, "use flask-cors for handling CORS requests"). + defaultValue(Boolean.FALSE.toString())); 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."). @@ -213,6 +217,9 @@ public abstract class AbstractPythonConnexionServerCodegen extends DefaultCodege additionalProperties.put(SUPPORT_PYTHON2, Boolean.TRUE); typeMapping.put("long", "long"); } + if (additionalProperties.containsKey(FEATURE_CORS)) { + setFeatureCORS((String) additionalProperties.get(FEATURE_CORS)); + } if (additionalProperties.containsKey(USE_NOSE)) { setUseNose((String) additionalProperties.get(USE_NOSE)); } @@ -236,6 +243,10 @@ public abstract class AbstractPythonConnexionServerCodegen extends DefaultCodege controllerPackage = packageName + "." + controllerPackage; } + public void setFeatureCORS(String val) { + this.featureCORS = Boolean.valueOf(val); + } + public void setUseNose(String val) { this.useNose = Boolean.valueOf(val); } diff --git a/modules/openapi-generator/src/main/resources/python-aiohttp/__init__main.mustache b/modules/openapi-generator/src/main/resources/python-aiohttp/__init__main.mustache index ba3fa3a6584..e185ac470d9 100644 --- a/modules/openapi-generator/src/main/resources/python-aiohttp/__init__main.mustache +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/__init__main.mustache @@ -1,6 +1,8 @@ import os import connexion - +{{#featureCORS}} +import aiohttp_cors +{{/featureCORS}} def main(): options = { @@ -12,4 +14,20 @@ def main(): arguments={'title': '{{appName}}'}, pythonic_params=True, pass_context_arg_name='request') + +{{#featureCORS}} + # Enable CORS for all origins. + cors = aiohttp_cors.setup(app.app, defaults={ + "*": aiohttp_cors.ResourceOptions( + allow_credentials=True, + expose_headers="*", + allow_headers="*", + ) + }) + + # Register all routers for CORS. + for route in list(app.app.router.routes()): + cors.add(route) + +{{/featureCORS}} app.run(port={{serverPort}}) 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 e0dd796ca9f..b828c50f4a2 100644 --- a/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache +++ b/modules/openapi-generator/src/main/resources/python-aiohttp/requirements.mustache @@ -7,3 +7,6 @@ connexion[aiohttp,swagger-ui] <= 2.3.0; python_version=="3.5" or python_version= werkzeug == 0.16.1; python_version=="3.5" or python_version=="3.4" swagger-ui-bundle == 0.0.6 aiohttp_jinja2 == 1.2.0 +{{#featureCORS}} +aiohttp_cors >= 0.7.0 +{{/featureCORS}} diff --git a/modules/openapi-generator/src/main/resources/python-blueplanet/app/requirements.mustache b/modules/openapi-generator/src/main/resources/python-blueplanet/app/requirements.mustache index d08b80acc5b..275787f38cc 100644 --- a/modules/openapi-generator/src/main/resources/python-blueplanet/app/requirements.mustache +++ b/modules/openapi-generator/src/main/resources/python-blueplanet/app/requirements.mustache @@ -7,3 +7,7 @@ setuptools >= 21.0.0 bp2hookutil==3.3.0 plansdk twigjack +{{#featureCORS}} +# should support both Python 2 and Python 3 +flask-cors >= 3.0.10 +{{/featureCORS}} diff --git a/modules/openapi-generator/src/main/resources/python-blueplanet/app/{{packageName}}/__main__.mustache b/modules/openapi-generator/src/main/resources/python-blueplanet/app/{{packageName}}/__main__.mustache index 10756347223..ab6835dac85 100644 --- a/modules/openapi-generator/src/main/resources/python-blueplanet/app/{{packageName}}/__main__.mustache +++ b/modules/openapi-generator/src/main/resources/python-blueplanet/app/{{packageName}}/__main__.mustache @@ -6,6 +6,9 @@ {{/supportPython2}} import connexion +{{#featureCORS}} +from flask_cors import CORS +{{/featureCORS}} from {{packageName}} import encoder @@ -14,6 +17,12 @@ def main(): app = connexion.App(__name__, specification_dir='./swagger/') app.app.json_encoder = encoder.JSONEncoder app.add_api('swagger.yaml', arguments={'title': '{{appName}}'}) + +{{#featureCORS}} + # add CORS support + CORS(app.app) + +{{/featureCORS}} app.run(port={{serverPort}}) diff --git a/modules/openapi-generator/src/main/resources/python-flask/__main__.mustache b/modules/openapi-generator/src/main/resources/python-flask/__main__.mustache index 37b7a8bab47..b6018a3af05 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/__main__.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/__main__.mustache @@ -6,6 +6,9 @@ {{/supportPython2}} import connexion +{{#featureCORS}} +from flask_cors import CORS +{{/featureCORS}} from {{packageName}} import encoder @@ -16,6 +19,12 @@ def main(): app.add_api('openapi.yaml', arguments={'title': '{{appName}}'}, pythonic_params=True) + +{{#featureCORS}} + # add CORS support + CORS(app.app) + +{{/featureCORS}} app.run(port={{serverPort}}) 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 6e3f7d524c2..6e2cf517819 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/requirements.mustache @@ -10,6 +10,10 @@ connexion[swagger-ui] == 2.4.0; python_version<="2.7" werkzeug == 0.16.1; python_version=="3.5" or python_version=="3.4" swagger-ui-bundle >= 0.0.2 python_dateutil >= 2.6.0 +{{#featureCORS}} +# should support both Python 2 and Python 3 +flask-cors >= 3.0.10 +{{/featureCORS}} {{#supportPython2}} typing >= 3.5.2.2 # For specs with timestamps, pyyaml 5.3 broke connexion's spec parsing in python 2. 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 index d9fcbb5db20..387eceb08ca 100644 --- a/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/__init__.py +++ b/samples/server/petstore/python-aiohttp-srclayout/src/openapi_server/__init__.py @@ -1,7 +1,6 @@ import os import connexion - def main(): options = { "swagger_ui": True @@ -12,4 +11,5 @@ def main(): arguments={'title': 'OpenAPI Petstore'}, pythonic_params=True, pass_context_arg_name='request') + app.run(port=8080) diff --git a/samples/server/petstore/python-aiohttp/openapi_server/__init__.py b/samples/server/petstore/python-aiohttp/openapi_server/__init__.py index d9fcbb5db20..387eceb08ca 100644 --- a/samples/server/petstore/python-aiohttp/openapi_server/__init__.py +++ b/samples/server/petstore/python-aiohttp/openapi_server/__init__.py @@ -1,7 +1,6 @@ import os import connexion - def main(): options = { "swagger_ui": True @@ -12,4 +11,5 @@ def main(): arguments={'title': 'OpenAPI Petstore'}, pythonic_params=True, pass_context_arg_name='request') + app.run(port=8080) diff --git a/samples/server/petstore/python-blueplanet/app/openapi_server/__main__.py b/samples/server/petstore/python-blueplanet/app/openapi_server/__main__.py index a97eee0948a..333f12d0795 100644 --- a/samples/server/petstore/python-blueplanet/app/openapi_server/__main__.py +++ b/samples/server/petstore/python-blueplanet/app/openapi_server/__main__.py @@ -9,6 +9,7 @@ def main(): app = connexion.App(__name__, specification_dir='./swagger/') app.app.json_encoder = encoder.JSONEncoder app.add_api('swagger.yaml', arguments={'title': 'OpenAPI Petstore'}) + app.run(port=8080) diff --git a/samples/server/petstore/python-flask/openapi_server/__main__.py b/samples/server/petstore/python-flask/openapi_server/__main__.py index 24b2141e14d..6045d0156f0 100644 --- a/samples/server/petstore/python-flask/openapi_server/__main__.py +++ b/samples/server/petstore/python-flask/openapi_server/__main__.py @@ -11,6 +11,7 @@ def main(): app.add_api('openapi.yaml', arguments={'title': 'OpenAPI Petstore'}, pythonic_params=True) + app.run(port=8080)