diff --git a/bin/configs/python-experimental-extensions-x-auth-alias.yaml b/bin/configs/python-experimental-extensions-x-auth-alias.yaml new file mode 100644 index 000000000000..beaa59aa0d2b --- /dev/null +++ b/bin/configs/python-experimental-extensions-x-auth-alias.yaml @@ -0,0 +1,6 @@ +generatorName: python-experimental +outputDir: samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/ +inputSpec: modules/openapi-generator/src/test/resources/3_0/extensions/x-auth-id-alias.yaml +templateDir: modules/openapi-generator/src/main/resources/python +additionalProperties: + packageName: x_auth_id_alias diff --git a/modules/openapi-generator/src/main/resources/python/configuration.mustache b/modules/openapi-generator/src/main/resources/python/configuration.mustache index d1705eed25d2..f74ae45f2888 100644 --- a/modules/openapi-generator/src/main/resources/python/configuration.mustache +++ b/modules/openapi-generator/src/main/resources/python/configuration.mustache @@ -423,15 +423,16 @@ conf = {{{packageName}}}.Configuration( self.__logger_format = value self.logger_formatter = logging.Formatter(self.__logger_format) - def get_api_key_with_prefix(self, identifier): + def get_api_key_with_prefix(self, identifier, alias=None): """Gets API key (with prefix if set). :param identifier: The identifier of apiKey. + :param alias: The alternative identifier of apiKey. :return: The token for api key authentication. """ if self.refresh_api_key_hook is not None: self.refresh_api_key_hook(self) - key = self.api_key.get(identifier) + key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) if key: prefix = self.api_key_prefix.get(identifier) if prefix: @@ -462,12 +463,15 @@ conf = {{{packageName}}}.Configuration( auth = {} {{#authMethods}} {{#isApiKey}} - if '{{keyParamName}}' in self.api_key: + if '{{name}}' in self.api_key{{#vendorExtensions.x-auth-id-alias}} or '{{.}}' in self.api_key{{/vendorExtensions.x-auth-id-alias}}: auth['{{name}}'] = { 'type': 'api_key', 'in': {{#isKeyInCookie}}'cookie'{{/isKeyInCookie}}{{#isKeyInHeader}}'header'{{/isKeyInHeader}}{{#isKeyInQuery}}'query'{{/isKeyInQuery}}, 'key': '{{keyParamName}}', - 'value': self.get_api_key_with_prefix('{{keyParamName}}') + 'value': self.get_api_key_with_prefix( + '{{name}}',{{#vendorExtensions.x-auth-id-alias}} + alias='{{.}}',{{/vendorExtensions.x-auth-id-alias}} + ), } {{/isApiKey}} {{#isBasic}} diff --git a/modules/openapi-generator/src/main/resources/python/python_doc_auth_partial.mustache b/modules/openapi-generator/src/main/resources/python/python_doc_auth_partial.mustache index 3086d770b183..460c2df938dd 100644 --- a/modules/openapi-generator/src/main/resources/python/python_doc_auth_partial.mustache +++ b/modules/openapi-generator/src/main/resources/python/python_doc_auth_partial.mustache @@ -95,11 +95,11 @@ configuration = {{{packageName}}}.Configuration( configuration = {{{packageName}}}.Configuration( host = "{{{basePath}}}", api_key = { - '{{{keyParamName}}}': 'YOUR_API_KEY' + '{{name}}': 'YOUR_API_KEY' } ) # Uncomment below to setup prefix (e.g. Bearer) for API key, if needed -# configuration.api_key_prefix['{{{keyParamName}}}'] = 'Bearer' +# configuration.api_key_prefix['{{name}}'] = 'Bearer' {{/isApiKey}} {{#isOAuth}} diff --git a/modules/openapi-generator/src/test/resources/3_0/extensions/x-auth-id-alias.yaml b/modules/openapi-generator/src/test/resources/3_0/extensions/x-auth-id-alias.yaml new file mode 100644 index 000000000000..50a7fcd7d497 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/extensions/x-auth-id-alias.yaml @@ -0,0 +1,113 @@ +openapi: 3.0.0 +info: + description: This specification shows how to use x-auth-id-alias extension for API keys. + version: 1.0.0 + title: OpenAPI Extension x-auth-id-alias + license: + name: Apache-2.0 + url: 'https://www.apache.org/licenses/LICENSE-2.0.html' +tags: + - name: usage + description: Show usage of x-auth-id-alias +components: + securitySchemes: + api_key: + type: apiKey + name: X-Api-Key + in: header + api_key_query: + type: apiKey + name: api_key + # Test key aliasing + x-auth-id-alias: api_key + in: query +paths: + /both: + get: + tags: + - usage + summary: Use both API keys + description: Use both API keys + operationId: bothKeys + security: + - api_key_query: [] + api_key: [] + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + /any: + get: + tags: + - usage + summary: Use any API key + description: Use any API key + operationId: anyKey + security: + - api_key_query: [] + - api_key: [] + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + /query: + get: + tags: + - usage + summary: Use API key in query + description: Use API key in query + operationId: keyInQuery + security: + - api_key_query: [] + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + /header: + get: + tags: + - usage + summary: Use API key in header + description: Use API key in header + operationId: keyInHeader + security: + - api_key: [] + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object +servers: + - url: 'http://{server}.swagger.io:{port}/v2' + description: petstore server + variables: + server: + enum: + - 'petstore' + - 'qa-petstore' + - 'dev-petstore' + default: 'petstore' + port: + enum: + - 80 + - 8080 + default: 80 + - url: https://localhost:8080/{version} + description: The local server + variables: + version: + enum: + - 'v1' + - 'v2' + default: 'v2' diff --git a/samples/client/petstore/python-asyncio/petstore_api/configuration.py b/samples/client/petstore/python-asyncio/petstore_api/configuration.py index 5c860e8c9547..9d67bb19de31 100644 --- a/samples/client/petstore/python-asyncio/petstore_api/configuration.py +++ b/samples/client/petstore/python-asyncio/petstore_api/configuration.py @@ -341,15 +341,16 @@ def logger_format(self, value): self.__logger_format = value self.logger_formatter = logging.Formatter(self.__logger_format) - def get_api_key_with_prefix(self, identifier): + def get_api_key_with_prefix(self, identifier, alias=None): """Gets API key (with prefix if set). :param identifier: The identifier of apiKey. + :param alias: The alternative identifier of apiKey. :return: The token for api key authentication. """ if self.refresh_api_key_hook is not None: self.refresh_api_key_hook(self) - key = self.api_key.get(identifier) + key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) if key: prefix = self.api_key_prefix.get(identifier) if prefix: @@ -383,14 +384,18 @@ def auth_settings(self): 'type': 'api_key', 'in': 'header', 'key': 'api_key', - 'value': self.get_api_key_with_prefix('api_key') + 'value': self.get_api_key_with_prefix( + 'api_key', + ), } if 'api_key_query' in self.api_key: auth['api_key_query'] = { 'type': 'api_key', 'in': 'query', 'key': 'api_key_query', - 'value': self.get_api_key_with_prefix('api_key_query') + 'value': self.get_api_key_with_prefix( + 'api_key_query', + ), } if self.username is not None and self.password is not None: auth['http_basic_test'] = { diff --git a/samples/client/petstore/python-experimental/petstore_api/configuration.py b/samples/client/petstore/python-experimental/petstore_api/configuration.py index 18085f8b7485..4d0f313ae0c8 100644 --- a/samples/client/petstore/python-experimental/petstore_api/configuration.py +++ b/samples/client/petstore/python-experimental/petstore_api/configuration.py @@ -345,15 +345,16 @@ def logger_format(self, value): self.__logger_format = value self.logger_formatter = logging.Formatter(self.__logger_format) - def get_api_key_with_prefix(self, identifier): + def get_api_key_with_prefix(self, identifier, alias=None): """Gets API key (with prefix if set). :param identifier: The identifier of apiKey. + :param alias: The alternative identifier of apiKey. :return: The token for api key authentication. """ if self.refresh_api_key_hook is not None: self.refresh_api_key_hook(self) - key = self.api_key.get(identifier) + key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) if key: prefix = self.api_key_prefix.get(identifier) if prefix: @@ -387,14 +388,18 @@ def auth_settings(self): 'type': 'api_key', 'in': 'header', 'key': 'api_key', - 'value': self.get_api_key_with_prefix('api_key') + 'value': self.get_api_key_with_prefix( + 'api_key', + ), } if 'api_key_query' in self.api_key: auth['api_key_query'] = { 'type': 'api_key', 'in': 'query', 'key': 'api_key_query', - 'value': self.get_api_key_with_prefix('api_key_query') + 'value': self.get_api_key_with_prefix( + 'api_key_query', + ), } if self.username is not None and self.password is not None: auth['http_basic_test'] = { diff --git a/samples/client/petstore/python-tornado/petstore_api/configuration.py b/samples/client/petstore/python-tornado/petstore_api/configuration.py index 18085f8b7485..4d0f313ae0c8 100644 --- a/samples/client/petstore/python-tornado/petstore_api/configuration.py +++ b/samples/client/petstore/python-tornado/petstore_api/configuration.py @@ -345,15 +345,16 @@ def logger_format(self, value): self.__logger_format = value self.logger_formatter = logging.Formatter(self.__logger_format) - def get_api_key_with_prefix(self, identifier): + def get_api_key_with_prefix(self, identifier, alias=None): """Gets API key (with prefix if set). :param identifier: The identifier of apiKey. + :param alias: The alternative identifier of apiKey. :return: The token for api key authentication. """ if self.refresh_api_key_hook is not None: self.refresh_api_key_hook(self) - key = self.api_key.get(identifier) + key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) if key: prefix = self.api_key_prefix.get(identifier) if prefix: @@ -387,14 +388,18 @@ def auth_settings(self): 'type': 'api_key', 'in': 'header', 'key': 'api_key', - 'value': self.get_api_key_with_prefix('api_key') + 'value': self.get_api_key_with_prefix( + 'api_key', + ), } if 'api_key_query' in self.api_key: auth['api_key_query'] = { 'type': 'api_key', 'in': 'query', 'key': 'api_key_query', - 'value': self.get_api_key_with_prefix('api_key_query') + 'value': self.get_api_key_with_prefix( + 'api_key_query', + ), } if self.username is not None and self.password is not None: auth['http_basic_test'] = { diff --git a/samples/client/petstore/python/petstore_api/configuration.py b/samples/client/petstore/python/petstore_api/configuration.py index 18085f8b7485..4d0f313ae0c8 100644 --- a/samples/client/petstore/python/petstore_api/configuration.py +++ b/samples/client/petstore/python/petstore_api/configuration.py @@ -345,15 +345,16 @@ def logger_format(self, value): self.__logger_format = value self.logger_formatter = logging.Formatter(self.__logger_format) - def get_api_key_with_prefix(self, identifier): + def get_api_key_with_prefix(self, identifier, alias=None): """Gets API key (with prefix if set). :param identifier: The identifier of apiKey. + :param alias: The alternative identifier of apiKey. :return: The token for api key authentication. """ if self.refresh_api_key_hook is not None: self.refresh_api_key_hook(self) - key = self.api_key.get(identifier) + key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) if key: prefix = self.api_key_prefix.get(identifier) if prefix: @@ -387,14 +388,18 @@ def auth_settings(self): 'type': 'api_key', 'in': 'header', 'key': 'api_key', - 'value': self.get_api_key_with_prefix('api_key') + 'value': self.get_api_key_with_prefix( + 'api_key', + ), } if 'api_key_query' in self.api_key: auth['api_key_query'] = { 'type': 'api_key', 'in': 'query', 'key': 'api_key_query', - 'value': self.get_api_key_with_prefix('api_key_query') + 'value': self.get_api_key_with_prefix( + 'api_key_query', + ), } if self.username is not None and self.password is not None: auth['http_basic_test'] = { diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.gitignore b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.gitignore new file mode 100644 index 000000000000..43995bd42fa2 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.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/openapi3/client/extensions/x-auth-id-alias/python-experimental/.gitlab-ci.yml b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.gitlab-ci.yml new file mode 100644 index 000000000000..475aaf9bb75b --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.gitlab-ci.yml @@ -0,0 +1,33 @@ +# ref: https://docs.gitlab.com/ee/ci/README.html + +stages: + - test + +.nosetest: + stage: test + script: + - pip install -r requirements.txt + - pip install -r test-requirements.txt + - pytest --cov=x_auth_id_alias + +nosetest-2.7: + extends: .nosetest + image: python:2.7-alpine +nosetest-3.3: + extends: .nosetest + image: python:3.3-alpine +nosetest-3.4: + extends: .nosetest + image: python:3.4-alpine +nosetest-3.5: + extends: .nosetest + image: python:3.5-alpine +nosetest-3.6: + extends: .nosetest + image: python:3.6-alpine +nosetest-3.7: + extends: .nosetest + image: python:3.7-alpine +nosetest-3.8: + extends: .nosetest + image: python:3.8-alpine diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.openapi-generator-ignore b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.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/openapi3/client/extensions/x-auth-id-alias/python-experimental/.openapi-generator/FILES b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.openapi-generator/FILES new file mode 100644 index 000000000000..93cf0c21cbdb --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.openapi-generator/FILES @@ -0,0 +1,23 @@ +.gitignore +.gitlab-ci.yml +.travis.yml +README.md +docs/UsageApi.md +git_push.sh +requirements.txt +setup.cfg +setup.py +test-requirements.txt +test/__init__.py +tox.ini +x_auth_id_alias/__init__.py +x_auth_id_alias/api/__init__.py +x_auth_id_alias/api/usage_api.py +x_auth_id_alias/api_client.py +x_auth_id_alias/apis/__init__.py +x_auth_id_alias/configuration.py +x_auth_id_alias/exceptions.py +x_auth_id_alias/model/__init__.py +x_auth_id_alias/model_utils.py +x_auth_id_alias/models/__init__.py +x_auth_id_alias/rest.py diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.openapi-generator/VERSION b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.openapi-generator/VERSION new file mode 100644 index 000000000000..d99e7162d01f --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.openapi-generator/VERSION @@ -0,0 +1 @@ +5.0.0-SNAPSHOT \ No newline at end of file diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.travis.yml b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.travis.yml new file mode 100644 index 000000000000..97a849816e36 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/.travis.yml @@ -0,0 +1,17 @@ +# ref: https://docs.travis-ci.com/user/languages/python +language: python +python: + - "2.7" + - "3.2" + - "3.3" + - "3.4" + - "3.5" + - "3.6" + - "3.7" + - "3.8" +# command to install dependencies +install: + - "pip install -r requirements.txt" + - "pip install -r test-requirements.txt" +# command to run tests +script: pytest --cov=x_auth_id_alias diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/README.md b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/README.md new file mode 100644 index 000000000000..38273d89a1dc --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/README.md @@ -0,0 +1,155 @@ +# x-auth-id-alias +This specification shows how to use x-auth-id-alias extension for API keys. + +This Python package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: + +- API version: 1.0.0 +- Package version: 1.0.0 +- Build package: org.openapitools.codegen.languages.PythonClientExperimentalCodegen + +## Requirements. + +Python 2.7 and 3.4+ + +## Installation & Usage +### pip install + +If the python package is hosted on a repository, you can install directly using: + +```sh +pip install git+https://github.com/GIT_USER_ID/GIT_REPO_ID.git +``` +(you may need to run `pip` with root permission: `sudo pip install git+https://github.com/GIT_USER_ID/GIT_REPO_ID.git`) + +Then import the package: +```python +import x_auth_id_alias +``` + +### Setuptools + +Install via [Setuptools](http://pypi.python.org/pypi/setuptools). + +```sh +python setup.py install --user +``` +(or `sudo python setup.py install` to install the package for all users) + +Then import the package: +```python +import x_auth_id_alias +``` + +## Getting Started + +Please follow the [installation procedure](#installation--usage) and then run the following: + +```python +from __future__ import print_function + +import time +import x_auth_id_alias +from pprint import pprint +from x_auth_id_alias.api import usage_api +# Defining the host is optional and defaults to http://petstore.swagger.io:80/v2 +# See configuration.py for a list of all supported configuration parameters. +configuration = x_auth_id_alias.Configuration( + host = "http://petstore.swagger.io:80/v2" +) + +# The client must configure the authentication and authorization parameters +# in accordance with the API server security policy. +# Examples for each auth method are provided below, use the example that +# satisfies your auth use case. + +# Configure API key authorization: api_key +configuration = x_auth_id_alias.Configuration( + host = "http://petstore.swagger.io:80/v2", + api_key = { + 'api_key': 'YOUR_API_KEY' + } +) +# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed +# configuration.api_key_prefix['api_key'] = 'Bearer' + +# Configure API key authorization: api_key_query +configuration = x_auth_id_alias.Configuration( + host = "http://petstore.swagger.io:80/v2", + api_key = { + 'api_key_query': 'YOUR_API_KEY' + } +) +# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed +# configuration.api_key_prefix['api_key_query'] = 'Bearer' + + +# Enter a context with an instance of the API client +with x_auth_id_alias.ApiClient(configuration) as api_client: + # Create an instance of the API class + api_instance = usage_api.UsageApi(api_client) + + try: + # Use any API key + api_response = api_instance.any_key() + pprint(api_response) + except x_auth_id_alias.ApiException as e: + print("Exception when calling UsageApi->any_key: %s\n" % e) +``` + +## Documentation for API Endpoints + +All URIs are relative to *http://petstore.swagger.io:80/v2* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*UsageApi* | [**any_key**](docs/UsageApi.md#any_key) | **GET** /any | Use any API key +*UsageApi* | [**both_keys**](docs/UsageApi.md#both_keys) | **GET** /both | Use both API keys +*UsageApi* | [**key_in_header**](docs/UsageApi.md#key_in_header) | **GET** /header | Use API key in header +*UsageApi* | [**key_in_query**](docs/UsageApi.md#key_in_query) | **GET** /query | Use API key in query + + +## Documentation For Models + + + +## Documentation For Authorization + + +## api_key + +- **Type**: API key +- **API key parameter name**: X-Api-Key +- **Location**: HTTP header + + +## api_key_query + +- **Type**: API key +- **API key parameter name**: api_key +- **Location**: URL query string + + +## Author + + + + +## Notes for Large OpenAPI documents +If the OpenAPI document is large, imports in x_auth_id_alias.apis and x_auth_id_alias.models may fail with a +RecursionError indicating the maximum recursion limit has been exceeded. In that case, there are a couple of solutions: + +Solution 1: +Use specific imports for apis and models like: +- `from x_auth_id_alias.api.default_api import DefaultApi` +- `from x_auth_id_alias.model.pet import Pet` + +Solution 1: +Before importing the package, adjust the maximum recursion limit as shown below: +``` +import sys +sys.setrecursionlimit(1500) +import x_auth_id_alias +from x_auth_id_alias.apis import * +from x_auth_id_alias.models import * +``` + diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/docs/UsageApi.md b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/docs/UsageApi.md new file mode 100644 index 000000000000..bea1ee7ce196 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/docs/UsageApi.md @@ -0,0 +1,330 @@ +# x_auth_id_alias.UsageApi + +All URIs are relative to *http://petstore.swagger.io:80/v2* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**any_key**](UsageApi.md#any_key) | **GET** /any | Use any API key +[**both_keys**](UsageApi.md#both_keys) | **GET** /both | Use both API keys +[**key_in_header**](UsageApi.md#key_in_header) | **GET** /header | Use API key in header +[**key_in_query**](UsageApi.md#key_in_query) | **GET** /query | Use API key in query + + +# **any_key** +> {str: (bool, date, datetime, dict, float, int, list, str, none_type)} any_key() + +Use any API key + +Use any API key + +### Example + +* Api Key Authentication (api_key): +* Api Key Authentication (api_key_query): +```python +from __future__ import print_function +import time +import x_auth_id_alias +from x_auth_id_alias.api import usage_api +from pprint import pprint +# Defining the host is optional and defaults to http://petstore.swagger.io:80/v2 +# See configuration.py for a list of all supported configuration parameters. +configuration = x_auth_id_alias.Configuration( + host = "http://petstore.swagger.io:80/v2" +) + +# The client must configure the authentication and authorization parameters +# in accordance with the API server security policy. +# Examples for each auth method are provided below, use the example that +# satisfies your auth use case. + +# Configure API key authorization: api_key +configuration = x_auth_id_alias.Configuration( + host = "http://petstore.swagger.io:80/v2", + api_key = { + 'api_key': 'YOUR_API_KEY' + } +) +# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed +# configuration.api_key_prefix['api_key'] = 'Bearer' + +# Configure API key authorization: api_key_query +configuration = x_auth_id_alias.Configuration( + host = "http://petstore.swagger.io:80/v2", + api_key = { + 'api_key_query': 'YOUR_API_KEY' + } +) +# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed +# configuration.api_key_prefix['api_key_query'] = 'Bearer' + +# Enter a context with an instance of the API client +with x_auth_id_alias.ApiClient(configuration) as api_client: + # Create an instance of the API class + api_instance = usage_api.UsageApi(api_client) + + # example, this endpoint has no required or optional parameters + try: + # Use any API key + api_response = api_instance.any_key() + pprint(api_response) + except x_auth_id_alias.ApiException as e: + print("Exception when calling UsageApi->any_key: %s\n" % e) +``` + +### Parameters +This endpoint does not need any parameter. + +### Return type + +**{str: (bool, date, datetime, dict, float, int, list, str, none_type)}** + +### Authorization + +[api_key](../README.md#api_key), [api_key_query](../README.md#api_key_query) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | successful operation | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **both_keys** +> {str: (bool, date, datetime, dict, float, int, list, str, none_type)} both_keys() + +Use both API keys + +Use both API keys + +### Example + +* Api Key Authentication (api_key): +* Api Key Authentication (api_key_query): +```python +from __future__ import print_function +import time +import x_auth_id_alias +from x_auth_id_alias.api import usage_api +from pprint import pprint +# Defining the host is optional and defaults to http://petstore.swagger.io:80/v2 +# See configuration.py for a list of all supported configuration parameters. +configuration = x_auth_id_alias.Configuration( + host = "http://petstore.swagger.io:80/v2" +) + +# The client must configure the authentication and authorization parameters +# in accordance with the API server security policy. +# Examples for each auth method are provided below, use the example that +# satisfies your auth use case. + +# Configure API key authorization: api_key +configuration = x_auth_id_alias.Configuration( + host = "http://petstore.swagger.io:80/v2", + api_key = { + 'api_key': 'YOUR_API_KEY' + } +) +# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed +# configuration.api_key_prefix['api_key'] = 'Bearer' + +# Configure API key authorization: api_key_query +configuration = x_auth_id_alias.Configuration( + host = "http://petstore.swagger.io:80/v2", + api_key = { + 'api_key_query': 'YOUR_API_KEY' + } +) +# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed +# configuration.api_key_prefix['api_key_query'] = 'Bearer' + +# Enter a context with an instance of the API client +with x_auth_id_alias.ApiClient(configuration) as api_client: + # Create an instance of the API class + api_instance = usage_api.UsageApi(api_client) + + # example, this endpoint has no required or optional parameters + try: + # Use both API keys + api_response = api_instance.both_keys() + pprint(api_response) + except x_auth_id_alias.ApiException as e: + print("Exception when calling UsageApi->both_keys: %s\n" % e) +``` + +### Parameters +This endpoint does not need any parameter. + +### Return type + +**{str: (bool, date, datetime, dict, float, int, list, str, none_type)}** + +### Authorization + +[api_key](../README.md#api_key), [api_key_query](../README.md#api_key_query) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | successful operation | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **key_in_header** +> {str: (bool, date, datetime, dict, float, int, list, str, none_type)} key_in_header() + +Use API key in header + +Use API key in header + +### Example + +* Api Key Authentication (api_key): +```python +from __future__ import print_function +import time +import x_auth_id_alias +from x_auth_id_alias.api import usage_api +from pprint import pprint +# Defining the host is optional and defaults to http://petstore.swagger.io:80/v2 +# See configuration.py for a list of all supported configuration parameters. +configuration = x_auth_id_alias.Configuration( + host = "http://petstore.swagger.io:80/v2" +) + +# The client must configure the authentication and authorization parameters +# in accordance with the API server security policy. +# Examples for each auth method are provided below, use the example that +# satisfies your auth use case. + +# Configure API key authorization: api_key +configuration = x_auth_id_alias.Configuration( + host = "http://petstore.swagger.io:80/v2", + api_key = { + 'api_key': 'YOUR_API_KEY' + } +) +# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed +# configuration.api_key_prefix['api_key'] = 'Bearer' + +# Enter a context with an instance of the API client +with x_auth_id_alias.ApiClient(configuration) as api_client: + # Create an instance of the API class + api_instance = usage_api.UsageApi(api_client) + + # example, this endpoint has no required or optional parameters + try: + # Use API key in header + api_response = api_instance.key_in_header() + pprint(api_response) + except x_auth_id_alias.ApiException as e: + print("Exception when calling UsageApi->key_in_header: %s\n" % e) +``` + +### Parameters +This endpoint does not need any parameter. + +### Return type + +**{str: (bool, date, datetime, dict, float, int, list, str, none_type)}** + +### Authorization + +[api_key](../README.md#api_key) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | successful operation | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **key_in_query** +> {str: (bool, date, datetime, dict, float, int, list, str, none_type)} key_in_query() + +Use API key in query + +Use API key in query + +### Example + +* Api Key Authentication (api_key_query): +```python +from __future__ import print_function +import time +import x_auth_id_alias +from x_auth_id_alias.api import usage_api +from pprint import pprint +# Defining the host is optional and defaults to http://petstore.swagger.io:80/v2 +# See configuration.py for a list of all supported configuration parameters. +configuration = x_auth_id_alias.Configuration( + host = "http://petstore.swagger.io:80/v2" +) + +# The client must configure the authentication and authorization parameters +# in accordance with the API server security policy. +# Examples for each auth method are provided below, use the example that +# satisfies your auth use case. + +# Configure API key authorization: api_key_query +configuration = x_auth_id_alias.Configuration( + host = "http://petstore.swagger.io:80/v2", + api_key = { + 'api_key_query': 'YOUR_API_KEY' + } +) +# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed +# configuration.api_key_prefix['api_key_query'] = 'Bearer' + +# Enter a context with an instance of the API client +with x_auth_id_alias.ApiClient(configuration) as api_client: + # Create an instance of the API class + api_instance = usage_api.UsageApi(api_client) + + # example, this endpoint has no required or optional parameters + try: + # Use API key in query + api_response = api_instance.key_in_query() + pprint(api_response) + except x_auth_id_alias.ApiException as e: + print("Exception when calling UsageApi->key_in_query: %s\n" % e) +``` + +### Parameters +This endpoint does not need any parameter. + +### Return type + +**{str: (bool, date, datetime, dict, float, int, list, str, none_type)}** + +### Authorization + +[api_key_query](../README.md#api_key_query) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | successful operation | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/git_push.sh b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/git_push.sh new file mode 100644 index 000000000000..ced3be2b0c7b --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/git_push.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-pestore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="github.com" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/requirements.txt b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/requirements.txt new file mode 100644 index 000000000000..a56bedffbf57 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/requirements.txt @@ -0,0 +1,7 @@ +nulltype +certifi >= 14.05.14 +future; python_version<="2.7" +six >= 1.10 +python_dateutil >= 2.5.3 +setuptools >= 21.0.0 +urllib3 >= 1.15.1 diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/setup.cfg b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/setup.cfg new file mode 100644 index 000000000000..11433ee875ab --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/setup.cfg @@ -0,0 +1,2 @@ +[flake8] +max-line-length=99 diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/setup.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/setup.py new file mode 100644 index 000000000000..d266f0ac09fb --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/setup.py @@ -0,0 +1,49 @@ +# coding: utf-8 + +""" + OpenAPI Extension x-auth-id-alias + + This specification shows how to use x-auth-id-alias extension for API keys. # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +from setuptools import setup, find_packages # noqa: H301 + +NAME = "x-auth-id-alias" +VERSION = "1.0.0" +# To install the library, run the following +# +# python setup.py install +# +# prerequisite: setuptools +# http://pypi.python.org/pypi/setuptools + +REQUIRES = [ + "urllib3 >= 1.15", + "six >= 1.10", + "certifi", + "python-dateutil", + "nulltype", +] +EXTRAS = {':python_version <= "2.7"': ['future']} + +setup( + name=NAME, + version=VERSION, + description="OpenAPI Extension x-auth-id-alias", + author="OpenAPI Generator community", + author_email="team@openapitools.org", + url="", + keywords=["OpenAPI", "OpenAPI-Generator", "OpenAPI Extension x-auth-id-alias"], + install_requires=REQUIRES, + extras_require=EXTRAS, + packages=find_packages(exclude=["test", "tests"]), + include_package_data=True, + license="Apache-2.0", + long_description="""\ + This specification shows how to use x-auth-id-alias extension for API keys. # noqa: E501 + """ +) diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/test-requirements.txt b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/test-requirements.txt new file mode 100644 index 000000000000..06f7754d2044 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/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 +mock; python_version<="2.7" \ No newline at end of file diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/test/__init__.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/test/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/test/test_usage_api.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/test/test_usage_api.py new file mode 100644 index 000000000000..f7d98a751b40 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/test/test_usage_api.py @@ -0,0 +1,60 @@ +# coding: utf-8 + +""" + OpenAPI Extension x-auth-id-alias + + This specification shows how to use x-auth-id-alias extension for API keys. # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +from __future__ import absolute_import + +import unittest + +import x_auth_id_alias +from x_auth_id_alias.api.usage_api import UsageApi # noqa: E501 + + +class TestUsageApi(unittest.TestCase): + """UsageApi unit test stubs""" + + def setUp(self): + self.api = UsageApi() # noqa: E501 + + def tearDown(self): + pass + + def test_any_key(self): + """Test case for any_key + + Use any API key # noqa: E501 + """ + pass + + def test_both_keys(self): + """Test case for both_keys + + Use both API keys # noqa: E501 + """ + pass + + def test_key_in_header(self): + """Test case for key_in_header + + Use API key in header # noqa: E501 + """ + pass + + def test_key_in_query(self): + """Test case for key_in_query + + Use API key in query # noqa: E501 + """ + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/tests/test_api_keys.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/tests/test_api_keys.py new file mode 100644 index 000000000000..4852fd4b9136 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/tests/test_api_keys.py @@ -0,0 +1,104 @@ +# coding: utf-8 + +""" + Test usage of x-auth-id-alias. +""" + +from __future__ import absolute_import + +import unittest + +import x_auth_id_alias +from x_auth_id_alias.api.usage_api import UsageApi + + +class TestUsageApi(unittest.TestCase): + """UsageApi unit test stubs""" + + def setUp(self): + self.configuration = x_auth_id_alias.Configuration( + host="http://localhost/", + api_key={"api_key": "SECRET_VALUE"}, + api_key_prefix={"api_key": "PREFIX"}, + ) + self.client = x_auth_id_alias.ApiClient(self.configuration) + self.api = UsageApi(self.client) + + def tearDown(self): + self.client.close() + + def test_any_key(self): + """Test case for any_key + + Use any API key # noqa: E501 + """ + + def request(*args, **kwargs): + assert ("api_key", "SECRET_VALUE") in kwargs["query_params"] + assert "PREFIX SECRET_VALUE" == kwargs["headers"]["X-Api-Key"] + raise RuntimeError("passed") + + self.client.request = request + + try: + self.api.any_key() + except RuntimeError as e: + assert "passed" == str(e) + + def test_both_keys(self): + """Test case for both_keys + + Use both API keys # noqa: E501 + """ + + def request(*args, **kwargs): + assert ("api_key", "SECRET_VALUE") in kwargs["query_params"] + assert "PREFIX SECRET_VALUE" == kwargs["headers"]["X-Api-Key"] + raise RuntimeError("passed") + + self.client.request = request + + try: + self.api.both_keys() + except RuntimeError as e: + assert "passed" == str(e) + + def test_key_in_header(self): + """Test case for key_in_header + + Use API key in header # noqa: E501 + """ + + def request(*args, **kwargs): + assert ("api_key", "SECRET_VALUE") not in kwargs["query_params"] + assert "PREFIX SECRET_VALUE" == kwargs["headers"]["X-Api-Key"] + raise RuntimeError("passed") + + self.client.request = request + + try: + self.api.key_in_header() + except RuntimeError as e: + assert "passed" == str(e) + + def test_key_in_query(self): + """Test case for key_in_query + + Use API key in query # noqa: E501 + """ + + def request(*args, **kwargs): + assert ("api_key", "SECRET_VALUE") in kwargs["query_params"] + assert "X-Api-Key" not in kwargs["headers"] + raise RuntimeError("passed") + + self.client.request = request + + try: + self.api.key_in_query() + except RuntimeError as e: + assert "passed" == str(e) + + +if __name__ == "__main__": + unittest.main() diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/tox.ini b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/tox.ini new file mode 100644 index 000000000000..1eebd7b9dd51 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/tox.ini @@ -0,0 +1,9 @@ +[tox] +envlist = py27, py3 + +[testenv] +deps=-r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +commands= + pytest --cov=x_auth_id_alias diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/__init__.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/__init__.py new file mode 100644 index 000000000000..7b41721baa3c --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/__init__.py @@ -0,0 +1,31 @@ +# coding: utf-8 + +# flake8: noqa + +""" + OpenAPI Extension x-auth-id-alias + + This specification shows how to use x-auth-id-alias extension for API keys. # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +from __future__ import absolute_import + +__version__ = "1.0.0" + +# import ApiClient +from x_auth_id_alias.api_client import ApiClient + +# import Configuration +from x_auth_id_alias.configuration import Configuration + +# import exceptions +from x_auth_id_alias.exceptions import OpenApiException +from x_auth_id_alias.exceptions import ApiAttributeError +from x_auth_id_alias.exceptions import ApiTypeError +from x_auth_id_alias.exceptions import ApiValueError +from x_auth_id_alias.exceptions import ApiKeyError +from x_auth_id_alias.exceptions import ApiException \ No newline at end of file diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/api/__init__.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/api/__init__.py new file mode 100644 index 000000000000..37e0c5bbdb68 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/api/__init__.py @@ -0,0 +1,6 @@ +from __future__ import absolute_import + +# flake8: noqa + +# import apis into api package +from x_auth_id_alias.api.usage_api import UsageApi diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/api/usage_api.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/api/usage_api.py new file mode 100644 index 000000000000..06a4ccfba181 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/api/usage_api.py @@ -0,0 +1,699 @@ +# coding: utf-8 + +""" + OpenAPI Extension x-auth-id-alias + + This specification shows how to use x-auth-id-alias extension for API keys. # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +from __future__ import absolute_import + +import re # noqa: F401 +import sys # noqa: F401 + +# python 2 and python 3 compatibility library +import six + +from x_auth_id_alias.api_client import ApiClient +from x_auth_id_alias.exceptions import ( + ApiTypeError, + ApiValueError +) +from x_auth_id_alias.model_utils import ( # noqa: F401 + check_allowed_values, + check_validations, + date, + datetime, + file_type, + int, + none_type, + str, + validate_and_convert_types +) + + +class UsageApi(object): + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + + def __any_key( + self, + **kwargs + ): + """Use any API key # noqa: E501 + + Use any API key # noqa: E501 + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.any_key(async_req=True) + >>> result = thread.get() + + + Keyword Args: + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (float/tuple): timeout setting for this request. If one + number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _host_index (int): specifies the index of the server + that we want to use. + Default is 0. + async_req (bool): execute request asynchronously + + Returns: + {str: (bool, date, datetime, dict, float, int, list, str, none_type)} + If the method is called asynchronously, returns the request + thread. + """ + kwargs['async_req'] = kwargs.get( + 'async_req', False + ) + kwargs['_return_http_data_only'] = kwargs.get( + '_return_http_data_only', True + ) + kwargs['_preload_content'] = kwargs.get( + '_preload_content', True + ) + kwargs['_request_timeout'] = kwargs.get( + '_request_timeout', None + ) + kwargs['_check_input_type'] = kwargs.get( + '_check_input_type', True + ) + kwargs['_check_return_type'] = kwargs.get( + '_check_return_type', True + ) + kwargs['_host_index'] = kwargs.get('_host_index', 0) + return self.call_with_http_info(**kwargs) + + self.any_key = Endpoint( + settings={ + 'response_type': ({str: (bool, date, datetime, dict, float, int, list, str, none_type)},), + 'auth': [ + 'api_key', + 'api_key_query' + ], + 'endpoint_path': '/any', + 'operation_id': 'any_key', + 'http_method': 'GET', + 'servers': [], + }, + params_map={ + 'all': [ + ], + 'required': [], + 'nullable': [ + ], + 'enum': [ + ], + 'validation': [ + ] + }, + root_map={ + 'validations': { + }, + 'allowed_values': { + }, + 'openapi_types': { + }, + 'attribute_map': { + }, + 'location_map': { + }, + 'collection_format_map': { + } + }, + headers_map={ + 'accept': [ + 'application/json' + ], + 'content_type': [], + }, + api_client=api_client, + callable=__any_key + ) + + def __both_keys( + self, + **kwargs + ): + """Use both API keys # noqa: E501 + + Use both API keys # noqa: E501 + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.both_keys(async_req=True) + >>> result = thread.get() + + + Keyword Args: + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (float/tuple): timeout setting for this request. If one + number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _host_index (int): specifies the index of the server + that we want to use. + Default is 0. + async_req (bool): execute request asynchronously + + Returns: + {str: (bool, date, datetime, dict, float, int, list, str, none_type)} + If the method is called asynchronously, returns the request + thread. + """ + kwargs['async_req'] = kwargs.get( + 'async_req', False + ) + kwargs['_return_http_data_only'] = kwargs.get( + '_return_http_data_only', True + ) + kwargs['_preload_content'] = kwargs.get( + '_preload_content', True + ) + kwargs['_request_timeout'] = kwargs.get( + '_request_timeout', None + ) + kwargs['_check_input_type'] = kwargs.get( + '_check_input_type', True + ) + kwargs['_check_return_type'] = kwargs.get( + '_check_return_type', True + ) + kwargs['_host_index'] = kwargs.get('_host_index', 0) + return self.call_with_http_info(**kwargs) + + self.both_keys = Endpoint( + settings={ + 'response_type': ({str: (bool, date, datetime, dict, float, int, list, str, none_type)},), + 'auth': [ + 'api_key', + 'api_key_query' + ], + 'endpoint_path': '/both', + 'operation_id': 'both_keys', + 'http_method': 'GET', + 'servers': [], + }, + params_map={ + 'all': [ + ], + 'required': [], + 'nullable': [ + ], + 'enum': [ + ], + 'validation': [ + ] + }, + root_map={ + 'validations': { + }, + 'allowed_values': { + }, + 'openapi_types': { + }, + 'attribute_map': { + }, + 'location_map': { + }, + 'collection_format_map': { + } + }, + headers_map={ + 'accept': [ + 'application/json' + ], + 'content_type': [], + }, + api_client=api_client, + callable=__both_keys + ) + + def __key_in_header( + self, + **kwargs + ): + """Use API key in header # noqa: E501 + + Use API key in header # noqa: E501 + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.key_in_header(async_req=True) + >>> result = thread.get() + + + Keyword Args: + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (float/tuple): timeout setting for this request. If one + number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _host_index (int): specifies the index of the server + that we want to use. + Default is 0. + async_req (bool): execute request asynchronously + + Returns: + {str: (bool, date, datetime, dict, float, int, list, str, none_type)} + If the method is called asynchronously, returns the request + thread. + """ + kwargs['async_req'] = kwargs.get( + 'async_req', False + ) + kwargs['_return_http_data_only'] = kwargs.get( + '_return_http_data_only', True + ) + kwargs['_preload_content'] = kwargs.get( + '_preload_content', True + ) + kwargs['_request_timeout'] = kwargs.get( + '_request_timeout', None + ) + kwargs['_check_input_type'] = kwargs.get( + '_check_input_type', True + ) + kwargs['_check_return_type'] = kwargs.get( + '_check_return_type', True + ) + kwargs['_host_index'] = kwargs.get('_host_index', 0) + return self.call_with_http_info(**kwargs) + + self.key_in_header = Endpoint( + settings={ + 'response_type': ({str: (bool, date, datetime, dict, float, int, list, str, none_type)},), + 'auth': [ + 'api_key' + ], + 'endpoint_path': '/header', + 'operation_id': 'key_in_header', + 'http_method': 'GET', + 'servers': [], + }, + params_map={ + 'all': [ + ], + 'required': [], + 'nullable': [ + ], + 'enum': [ + ], + 'validation': [ + ] + }, + root_map={ + 'validations': { + }, + 'allowed_values': { + }, + 'openapi_types': { + }, + 'attribute_map': { + }, + 'location_map': { + }, + 'collection_format_map': { + } + }, + headers_map={ + 'accept': [ + 'application/json' + ], + 'content_type': [], + }, + api_client=api_client, + callable=__key_in_header + ) + + def __key_in_query( + self, + **kwargs + ): + """Use API key in query # noqa: E501 + + Use API key in query # noqa: E501 + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.key_in_query(async_req=True) + >>> result = thread.get() + + + Keyword Args: + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (float/tuple): timeout setting for this request. If one + number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _host_index (int): specifies the index of the server + that we want to use. + Default is 0. + async_req (bool): execute request asynchronously + + Returns: + {str: (bool, date, datetime, dict, float, int, list, str, none_type)} + If the method is called asynchronously, returns the request + thread. + """ + kwargs['async_req'] = kwargs.get( + 'async_req', False + ) + kwargs['_return_http_data_only'] = kwargs.get( + '_return_http_data_only', True + ) + kwargs['_preload_content'] = kwargs.get( + '_preload_content', True + ) + kwargs['_request_timeout'] = kwargs.get( + '_request_timeout', None + ) + kwargs['_check_input_type'] = kwargs.get( + '_check_input_type', True + ) + kwargs['_check_return_type'] = kwargs.get( + '_check_return_type', True + ) + kwargs['_host_index'] = kwargs.get('_host_index', 0) + return self.call_with_http_info(**kwargs) + + self.key_in_query = Endpoint( + settings={ + 'response_type': ({str: (bool, date, datetime, dict, float, int, list, str, none_type)},), + 'auth': [ + 'api_key_query' + ], + 'endpoint_path': '/query', + 'operation_id': 'key_in_query', + 'http_method': 'GET', + 'servers': [], + }, + params_map={ + 'all': [ + ], + 'required': [], + 'nullable': [ + ], + 'enum': [ + ], + 'validation': [ + ] + }, + root_map={ + 'validations': { + }, + 'allowed_values': { + }, + 'openapi_types': { + }, + 'attribute_map': { + }, + 'location_map': { + }, + 'collection_format_map': { + } + }, + headers_map={ + 'accept': [ + 'application/json' + ], + 'content_type': [], + }, + api_client=api_client, + callable=__key_in_query + ) + + +class Endpoint(object): + def __init__(self, settings=None, params_map=None, root_map=None, + headers_map=None, api_client=None, callable=None): + """Creates an endpoint + + Args: + settings (dict): see below key value pairs + 'response_type' (tuple/None): response type + 'auth' (list): a list of auth type keys + 'endpoint_path' (str): the endpoint path + 'operation_id' (str): endpoint string identifier + 'http_method' (str): POST/PUT/PATCH/GET etc + 'servers' (list): list of str servers that this endpoint is at + params_map (dict): see below key value pairs + 'all' (list): list of str endpoint parameter names + 'required' (list): list of required parameter names + 'nullable' (list): list of nullable parameter names + 'enum' (list): list of parameters with enum values + 'validation' (list): list of parameters with validations + root_map + 'validations' (dict): the dict mapping endpoint parameter tuple + paths to their validation dictionaries + 'allowed_values' (dict): the dict mapping endpoint parameter + tuple paths to their allowed_values (enum) dictionaries + 'openapi_types' (dict): param_name to openapi type + 'attribute_map' (dict): param_name to camelCase name + 'location_map' (dict): param_name to 'body', 'file', 'form', + 'header', 'path', 'query' + collection_format_map (dict): param_name to `csv` etc. + headers_map (dict): see below key value pairs + 'accept' (list): list of Accept header strings + 'content_type' (list): list of Content-Type header strings + api_client (ApiClient) api client instance + callable (function): the function which is invoked when the + Endpoint is called + """ + self.settings = settings + self.params_map = params_map + self.params_map['all'].extend([ + 'async_req', + '_host_index', + '_preload_content', + '_request_timeout', + '_return_http_data_only', + '_check_input_type', + '_check_return_type' + ]) + self.params_map['nullable'].extend(['_request_timeout']) + self.validations = root_map['validations'] + self.allowed_values = root_map['allowed_values'] + self.openapi_types = root_map['openapi_types'] + extra_types = { + 'async_req': (bool,), + '_host_index': (int,), + '_preload_content': (bool,), + '_request_timeout': (none_type, int, (int,), [int]), + '_return_http_data_only': (bool,), + '_check_input_type': (bool,), + '_check_return_type': (bool,) + } + self.openapi_types.update(extra_types) + self.attribute_map = root_map['attribute_map'] + self.location_map = root_map['location_map'] + self.collection_format_map = root_map['collection_format_map'] + self.headers_map = headers_map + self.api_client = api_client + self.callable = callable + + def __validate_inputs(self, kwargs): + for param in self.params_map['enum']: + if param in kwargs: + check_allowed_values( + self.allowed_values, + (param,), + kwargs[param] + ) + + for param in self.params_map['validation']: + if param in kwargs: + check_validations( + self.validations, + (param,), + kwargs[param], + configuration=self.api_client.configuration + ) + + if kwargs['_check_input_type'] is False: + return + + for key, value in six.iteritems(kwargs): + fixed_val = validate_and_convert_types( + value, + self.openapi_types[key], + [key], + False, + kwargs['_check_input_type'], + configuration=self.api_client.configuration + ) + kwargs[key] = fixed_val + + def __gather_params(self, kwargs): + params = { + 'body': None, + 'collection_format': {}, + 'file': {}, + 'form': [], + 'header': {}, + 'path': {}, + 'query': [] + } + + for param_name, param_value in six.iteritems(kwargs): + param_location = self.location_map.get(param_name) + if param_location is None: + continue + if param_location: + if param_location == 'body': + params['body'] = param_value + continue + base_name = self.attribute_map[param_name] + if (param_location == 'form' and + self.openapi_types[param_name] == (file_type,)): + params['file'][param_name] = [param_value] + elif (param_location == 'form' and + self.openapi_types[param_name] == ([file_type],)): + # param_value is already a list + params['file'][param_name] = param_value + elif param_location in {'form', 'query'}: + param_value_full = (base_name, param_value) + params[param_location].append(param_value_full) + if param_location not in {'form', 'query'}: + params[param_location][base_name] = param_value + collection_format = self.collection_format_map.get(param_name) + if collection_format: + params['collection_format'][base_name] = collection_format + + return params + + def __call__(self, *args, **kwargs): + """ This method is invoked when endpoints are called + Example: + pet_api = PetApi() + pet_api.add_pet # this is an instance of the class Endpoint + pet_api.add_pet() # this invokes pet_api.add_pet.__call__() + which then invokes the callable functions stored in that endpoint at + pet_api.add_pet.callable or self.callable in this class + """ + return self.callable(self, *args, **kwargs) + + def call_with_http_info(self, **kwargs): + + try: + _host = self.settings['servers'][kwargs['_host_index']] + except IndexError: + if self.settings['servers']: + raise ApiValueError( + "Invalid host index. Must be 0 <= index < %s" % + len(self.settings['servers']) + ) + _host = None + + for key, value in six.iteritems(kwargs): + if key not in self.params_map['all']: + raise ApiTypeError( + "Got an unexpected parameter '%s'" + " to method `%s`" % + (key, self.settings['operation_id']) + ) + # only throw this nullable ApiValueError if _check_input_type + # is False, if _check_input_type==True we catch this case + # in self.__validate_inputs + if (key not in self.params_map['nullable'] and value is None + and kwargs['_check_input_type'] is False): + raise ApiValueError( + "Value may not be None for non-nullable parameter `%s`" + " when calling `%s`" % + (key, self.settings['operation_id']) + ) + + for key in self.params_map['required']: + if key not in kwargs.keys(): + raise ApiValueError( + "Missing the required parameter `%s` when calling " + "`%s`" % (key, self.settings['operation_id']) + ) + + self.__validate_inputs(kwargs) + + params = self.__gather_params(kwargs) + + accept_headers_list = self.headers_map['accept'] + if accept_headers_list: + params['header']['Accept'] = self.api_client.select_header_accept( + accept_headers_list) + + content_type_headers_list = self.headers_map['content_type'] + if content_type_headers_list: + header_list = self.api_client.select_header_content_type( + content_type_headers_list) + params['header']['Content-Type'] = header_list + + return self.api_client.call_api( + self.settings['endpoint_path'], self.settings['http_method'], + params['path'], + params['query'], + params['header'], + body=params['body'], + post_params=params['form'], + files=params['file'], + response_type=self.settings['response_type'], + auth_settings=self.settings['auth'], + async_req=kwargs['async_req'], + _check_type=kwargs['_check_return_type'], + _return_http_data_only=kwargs['_return_http_data_only'], + _preload_content=kwargs['_preload_content'], + _request_timeout=kwargs['_request_timeout'], + _host=_host, + collection_formats=params['collection_format']) diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/api_client.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/api_client.py new file mode 100644 index 000000000000..b4001e33f95a --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/api_client.py @@ -0,0 +1,578 @@ +# coding: utf-8 +""" + OpenAPI Extension x-auth-id-alias + + This specification shows how to use x-auth-id-alias extension for API keys. # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + +from __future__ import absolute_import + +import json +import atexit +import mimetypes +from multiprocessing.pool import ThreadPool +import os +import re + +# python 2 and python 3 compatibility library +import six +from six.moves.urllib.parse import quote + +from x_auth_id_alias import rest +from x_auth_id_alias.configuration import Configuration +from x_auth_id_alias.exceptions import ApiValueError, ApiException +from x_auth_id_alias.model_utils import ( + ModelNormal, + ModelSimple, + ModelComposed, + date, + datetime, + deserialize_file, + file_type, + model_to_dict, + str, + validate_and_convert_types +) + + +class ApiClient(object): + """Generic API client for OpenAPI client library builds. + + OpenAPI generic API client. This client handles the client- + server communication, and is invariant across implementations. Specifics of + the methods and models for each application are generated from the OpenAPI + templates. + + NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + Do not edit the class manually. + + :param configuration: .Configuration object for this client + :param header_name: a header to pass when making calls to the API. + :param header_value: a header value to pass when making calls to + the API. + :param cookie: a cookie to include in the header when making calls + to the API + :param pool_threads: The number of threads to use for async requests + to the API. More threads means more concurrent API requests. + """ + + # six.binary_type python2=str, python3=bytes + # six.text_type python2=unicode, python3=str + PRIMITIVE_TYPES = ( + (float, bool, six.binary_type, six.text_type) + six.integer_types + ) + _pool = None + + def __init__(self, configuration=None, header_name=None, header_value=None, + cookie=None, pool_threads=1): + if configuration is None: + configuration = Configuration() + self.configuration = configuration + self.pool_threads = pool_threads + + self.rest_client = rest.RESTClientObject(configuration) + self.default_headers = {} + if header_name is not None: + self.default_headers[header_name] = header_value + self.cookie = cookie + # Set default User-Agent. + self.user_agent = 'OpenAPI-Generator/1.0.0/python' + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + def close(self): + if self._pool: + self._pool.close() + self._pool.join() + self._pool = None + if hasattr(atexit, 'unregister'): + atexit.unregister(self.close) + + @property + def pool(self): + """Create thread pool on first request + avoids instantiating unused threadpool for blocking clients. + """ + if self._pool is None: + atexit.register(self.close) + self._pool = ThreadPool(self.pool_threads) + return self._pool + + @property + def user_agent(self): + """User agent for this API client""" + return self.default_headers['User-Agent'] + + @user_agent.setter + def user_agent(self, value): + self.default_headers['User-Agent'] = value + + def set_default_header(self, header_name, header_value): + self.default_headers[header_name] = header_value + + def __call_api( + self, resource_path, method, path_params=None, + query_params=None, header_params=None, body=None, post_params=None, + files=None, response_type=None, auth_settings=None, + _return_http_data_only=None, collection_formats=None, + _preload_content=True, _request_timeout=None, _host=None, + _check_type=None): + + config = self.configuration + + # header parameters + header_params = header_params or {} + header_params.update(self.default_headers) + if self.cookie: + header_params['Cookie'] = self.cookie + if header_params: + header_params = self.sanitize_for_serialization(header_params) + header_params = dict(self.parameters_to_tuples(header_params, + collection_formats)) + + # path parameters + if path_params: + path_params = self.sanitize_for_serialization(path_params) + path_params = self.parameters_to_tuples(path_params, + collection_formats) + for k, v in path_params: + # specified safe chars, encode everything + resource_path = resource_path.replace( + '{%s}' % k, + quote(str(v), safe=config.safe_chars_for_path_param) + ) + + # query parameters + if query_params: + query_params = self.sanitize_for_serialization(query_params) + query_params = self.parameters_to_tuples(query_params, + collection_formats) + + # post parameters + if post_params or files: + post_params = post_params if post_params else [] + post_params = self.sanitize_for_serialization(post_params) + post_params = self.parameters_to_tuples(post_params, + collection_formats) + post_params.extend(self.files_parameters(files)) + + # body + if body: + body = self.sanitize_for_serialization(body) + + # auth setting + self.update_params_for_auth(header_params, query_params, + auth_settings, resource_path, method, body) + + # request url + if _host is None: + url = self.configuration.host + resource_path + else: + # use server/host defined in path or operation instead + url = _host + resource_path + + try: + # perform request and return response + response_data = self.request( + method, url, query_params=query_params, headers=header_params, + post_params=post_params, body=body, + _preload_content=_preload_content, + _request_timeout=_request_timeout) + except ApiException as e: + e.body = e.body.decode('utf-8') if six.PY3 else e.body + raise e + + content_type = response_data.getheader('content-type') + + self.last_response = response_data + + return_data = response_data + + if not _preload_content: + return (return_data) + return return_data + + if six.PY3 and response_type not in ["file", "bytes"]: + match = None + if content_type is not None: + match = re.search(r"charset=([a-zA-Z\-\d]+)[\s\;]?", content_type) + encoding = match.group(1) if match else "utf-8" + response_data.data = response_data.data.decode(encoding) + + # deserialize response data + if response_type: + return_data = self.deserialize( + response_data, + response_type, + _check_type + ) + else: + return_data = None + + if _return_http_data_only: + return (return_data) + else: + return (return_data, response_data.status, + response_data.getheaders()) + + def sanitize_for_serialization(self, obj): + """Builds a JSON POST object. + + If obj is None, return None. + If obj is str, int, long, float, bool, return directly. + If obj is datetime.datetime, datetime.date + convert to string in iso8601 format. + If obj is list, sanitize each element in the list. + If obj is dict, return the dict. + If obj is OpenAPI model, return the properties dict. + + :param obj: The data to serialize. + :return: The serialized form of data. + """ + if obj is None: + return None + elif isinstance(obj, self.PRIMITIVE_TYPES): + return obj + elif isinstance(obj, list): + return [self.sanitize_for_serialization(sub_obj) + for sub_obj in obj] + elif isinstance(obj, tuple): + return tuple(self.sanitize_for_serialization(sub_obj) + for sub_obj in obj) + elif isinstance(obj, (datetime, date)): + return obj.isoformat() + + if isinstance(obj, dict): + obj_dict = obj + elif isinstance(obj, ModelNormal) or isinstance(obj, ModelComposed): + # Convert model obj to dict + # Convert attribute name to json key in + # model definition for request + obj_dict = model_to_dict(obj, serialize=True) + elif isinstance(obj, ModelSimple): + return self.sanitize_for_serialization(obj.value) + + return {key: self.sanitize_for_serialization(val) + for key, val in six.iteritems(obj_dict)} + + def deserialize(self, response, response_type, _check_type): + """Deserializes response into an object. + + :param response: RESTResponse object to be deserialized. + :param response_type: For the response, a tuple containing: + valid classes + a list containing valid classes (for list schemas) + a dict containing a tuple of valid classes as the value + Example values: + (str,) + (Pet,) + (float, none_type) + ([int, none_type],) + ({str: (bool, str, int, float, date, datetime, str, none_type)},) + :param _check_type: boolean, whether to check the types of the data + received from the server + :type _check_type: bool + + :return: deserialized object. + """ + # handle file downloading + # save response body into a tmp file and return the instance + if response_type == (file_type,): + content_disposition = response.getheader("Content-Disposition") + return deserialize_file(response.data, self.configuration, + content_disposition=content_disposition) + + # fetch data from response object + try: + received_data = json.loads(response.data) + except ValueError: + received_data = response.data + + # store our data under the key of 'received_data' so users have some + # context if they are deserializing a string and the data type is wrong + deserialized_data = validate_and_convert_types( + received_data, + response_type, + ['received_data'], + True, + _check_type, + configuration=self.configuration + ) + return deserialized_data + + def call_api(self, resource_path, method, + path_params=None, query_params=None, header_params=None, + body=None, post_params=None, files=None, + response_type=None, auth_settings=None, async_req=None, + _return_http_data_only=None, collection_formats=None, + _preload_content=True, _request_timeout=None, _host=None, + _check_type=None): + """Makes the HTTP request (synchronous) and returns deserialized data. + + To make an async_req request, set the async_req parameter. + + :param resource_path: Path to method endpoint. + :param method: Method to call. + :param path_params: Path parameters in the url. + :param query_params: Query parameters in the url. + :param header_params: Header parameters to be + placed in the request header. + :param body: Request body. + :param post_params dict: Request post form parameters, + for `application/x-www-form-urlencoded`, `multipart/form-data`. + :param auth_settings list: Auth Settings names for the request. + :param response_type: For the response, a tuple containing: + valid classes + a list containing valid classes (for list schemas) + a dict containing a tuple of valid classes as the value + Example values: + (str,) + (Pet,) + (float, none_type) + ([int, none_type],) + ({str: (bool, str, int, float, date, datetime, str, none_type)},) + :param files: key -> field name, value -> a list of open file + objects for `multipart/form-data`. + :type files: dict + :param async_req bool: execute request asynchronously + :type async_req: bool, optional + :param _return_http_data_only: response data without head status code + and headers + :type _return_http_data_only: bool, optional + :param collection_formats: dict of collection formats for path, query, + header, and post parameters. + :type collection_formats: dict, optional + :param _preload_content: if False, the urllib3.HTTPResponse object will + be returned without reading/decoding response + data. Default is True. + :type _preload_content: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _check_type: boolean describing if the data back from the server + should have its type checked. + :type _check_type: bool, optional + :return: + If async_req parameter is True, + the request will be called asynchronously. + The method will return the request thread. + If parameter async_req is False or missing, + then the method will return the response directly. + """ + if not async_req: + return self.__call_api(resource_path, method, + path_params, query_params, header_params, + body, post_params, files, + response_type, auth_settings, + _return_http_data_only, collection_formats, + _preload_content, _request_timeout, _host, + _check_type) + + return self.pool.apply_async(self.__call_api, (resource_path, + method, path_params, + query_params, + header_params, body, + post_params, files, + response_type, + auth_settings, + _return_http_data_only, + collection_formats, + _preload_content, + _request_timeout, + _host, _check_type)) + + def request(self, method, url, query_params=None, headers=None, + post_params=None, body=None, _preload_content=True, + _request_timeout=None): + """Makes the HTTP request using RESTClient.""" + if method == "GET": + return self.rest_client.GET(url, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + headers=headers) + elif method == "HEAD": + return self.rest_client.HEAD(url, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + headers=headers) + elif method == "OPTIONS": + return self.rest_client.OPTIONS(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "POST": + return self.rest_client.POST(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "PUT": + return self.rest_client.PUT(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "PATCH": + return self.rest_client.PATCH(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "DELETE": + return self.rest_client.DELETE(url, + query_params=query_params, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + else: + raise ApiValueError( + "http method must be `GET`, `HEAD`, `OPTIONS`," + " `POST`, `PATCH`, `PUT` or `DELETE`." + ) + + def parameters_to_tuples(self, params, collection_formats): + """Get parameters as list of tuples, formatting collections. + + :param params: Parameters as dict or list of two-tuples + :param dict collection_formats: Parameter collection formats + :return: Parameters as list of tuples, collections formatted + """ + new_params = [] + if collection_formats is None: + collection_formats = {} + for k, v in six.iteritems(params) if isinstance(params, dict) else params: # noqa: E501 + if k in collection_formats: + collection_format = collection_formats[k] + if collection_format == 'multi': + new_params.extend((k, value) for value in v) + else: + if collection_format == 'ssv': + delimiter = ' ' + elif collection_format == 'tsv': + delimiter = '\t' + elif collection_format == 'pipes': + delimiter = '|' + else: # csv is the default + delimiter = ',' + new_params.append( + (k, delimiter.join(str(value) for value in v))) + else: + new_params.append((k, v)) + return new_params + + def files_parameters(self, files=None): + """Builds form parameters. + + :param files: None or a dict with key=param_name and + value is a list of open file objects + :return: List of tuples of form parameters with file data + """ + if files is None: + return [] + + params = [] + for param_name, file_instances in six.iteritems(files): + if file_instances is None: + # if the file field is nullable, skip None values + continue + for file_instance in file_instances: + if file_instance is None: + # if the file field is nullable, skip None values + continue + if file_instance.closed is True: + raise ApiValueError( + "Cannot read a closed file. The passed in file_type " + "for %s must be open." % param_name + ) + filename = os.path.basename(file_instance.name) + filedata = file_instance.read() + mimetype = (mimetypes.guess_type(filename)[0] or + 'application/octet-stream') + params.append( + tuple([param_name, tuple([filename, filedata, mimetype])])) + file_instance.close() + + return params + + def select_header_accept(self, accepts): + """Returns `Accept` based on an array of accepts provided. + + :param accepts: List of headers. + :return: Accept (e.g. application/json). + """ + if not accepts: + return + + accepts = [x.lower() for x in accepts] + + if 'application/json' in accepts: + return 'application/json' + else: + return ', '.join(accepts) + + def select_header_content_type(self, content_types): + """Returns `Content-Type` based on an array of content_types provided. + + :param content_types: List of content-types. + :return: Content-Type (e.g. application/json). + """ + if not content_types: + return 'application/json' + + content_types = [x.lower() for x in content_types] + + if 'application/json' in content_types or '*/*' in content_types: + return 'application/json' + else: + return content_types[0] + + def update_params_for_auth(self, headers, querys, auth_settings, + resource_path, method, body): + """Updates header and query params based on authentication setting. + + :param headers: Header parameters dict to be updated. + :param querys: Query parameters tuple list to be updated. + :param auth_settings: Authentication setting identifiers list. + :param resource_path: A string representation of the HTTP request resource path. + :param method: A string representation of the HTTP request method. + :param body: A object representing the body of the HTTP request. + The object type is the return value of sanitize_for_serialization(). + """ + if not auth_settings: + return + + for auth in auth_settings: + auth_setting = self.configuration.auth_settings().get(auth) + if auth_setting: + if auth_setting['in'] == 'cookie': + headers['Cookie'] = auth_setting['value'] + elif auth_setting['in'] == 'header': + if auth_setting['type'] != 'http-signature': + headers[auth_setting['key']] = auth_setting['value'] + elif auth_setting['in'] == 'query': + querys.append((auth_setting['key'], auth_setting['value'])) + else: + raise ApiValueError( + 'Authentication token must be in `query` or `header`' + ) diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/apis/__init__.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/apis/__init__.py new file mode 100644 index 000000000000..c1333f3c2ad1 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/apis/__init__.py @@ -0,0 +1,18 @@ +# coding: utf-8 + +# flake8: noqa + +# Import all APIs into this package. +# If you have many APIs here with many many models used in each API this may +# raise a `RecursionError`. +# In order to avoid this, import only the API that you directly need like: +# +# from .api.pet_api import PetApi +# +# or import this package, but before doing it, use: +# +# import sys +# sys.setrecursionlimit(n) + +# Import APIs into API package: +from x_auth_id_alias.api.usage_api import UsageApi diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/configuration.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/configuration.py new file mode 100644 index 000000000000..f86db086690a --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/configuration.py @@ -0,0 +1,478 @@ +# coding: utf-8 + +""" + OpenAPI Extension x-auth-id-alias + + This specification shows how to use x-auth-id-alias extension for API keys. # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +from __future__ import absolute_import + +import copy +import logging +import multiprocessing +import sys +import urllib3 + +import six +from six.moves import http_client as httplib +from x_auth_id_alias.exceptions import ApiValueError + + +JSON_SCHEMA_VALIDATION_KEYWORDS = { + 'multipleOf', 'maximum', 'exclusiveMaximum', + 'minimum', 'exclusiveMinimum', 'maxLength', + 'minLength', 'pattern', 'maxItems', 'minItems' +} + +class Configuration(object): + """NOTE: This class is auto generated by OpenAPI Generator + + Ref: https://openapi-generator.tech + Do not edit the class manually. + + :param host: Base url + :param api_key: Dict to store API key(s). + Each entry in the dict specifies an API key. + The dict key is the name of the security scheme in the OAS specification. + The dict value is the API key secret. + :param api_key_prefix: Dict to store API prefix (e.g. Bearer) + The dict key is the name of the security scheme in the OAS specification. + The dict value is an API key prefix when generating the auth data. + :param username: Username for HTTP basic authentication + :param password: Password for HTTP basic authentication + :param discard_unknown_keys: Boolean value indicating whether to discard + unknown properties. A server may send a response that includes additional + properties that are not known by the client in the following scenarios: + 1. The OpenAPI document is incomplete, i.e. it does not match the server + implementation. + 2. The client was generated using an older version of the OpenAPI document + and the server has been upgraded since then. + If a schema in the OpenAPI document defines the additionalProperties attribute, + then all undeclared properties received by the server are injected into the + additional properties map. In that case, there are undeclared properties, and + nothing to discard. + :param disabled_client_side_validations (string): Comma-separated list of + JSON schema validation keywords to disable JSON schema structural validation + rules. The following keywords may be specified: multipleOf, maximum, + exclusiveMaximum, minimum, exclusiveMinimum, maxLength, minLength, pattern, + maxItems, minItems. + By default, the validation is performed for data generated locally by the client + and data received from the server, independent of any validation performed by + the server side. If the input data does not satisfy the JSON schema validation + rules specified in the OpenAPI document, an exception is raised. + If disabled_client_side_validations is set, structural validation is + disabled. This can be useful to troubleshoot data validation problem, such as + when the OpenAPI document validation rules do not match the actual API data + received by the server. + + :Example: + + API Key Authentication Example. + Given the following security scheme in the OpenAPI specification: + components: + securitySchemes: + cookieAuth: # name for the security scheme + type: apiKey + in: cookie + name: JSESSIONID # cookie name + + You can programmatically set the cookie: + +conf = x_auth_id_alias.Configuration( + api_key={'cookieAuth': 'abc123'} + api_key_prefix={'cookieAuth': 'JSESSIONID'} +) + + The following cookie will be added to the HTTP request: + Cookie: JSESSIONID abc123 + """ + + _default = None + + def __init__(self, host="http://petstore.swagger.io:80/v2", + api_key=None, api_key_prefix=None, + username=None, password=None, + discard_unknown_keys=False, + disabled_client_side_validations="", + ): + """Constructor + """ + self.host = host + """Default Base url + """ + self.temp_folder_path = None + """Temp file folder for downloading files + """ + # Authentication Settings + self.api_key = {} + if api_key: + self.api_key = api_key + """dict to store API key(s) + """ + self.api_key_prefix = {} + if api_key_prefix: + self.api_key_prefix = api_key_prefix + """dict to store API prefix (e.g. Bearer) + """ + self.refresh_api_key_hook = None + """function hook to refresh API key if expired + """ + self.username = username + """Username for HTTP basic authentication + """ + self.password = password + """Password for HTTP basic authentication + """ + self.discard_unknown_keys = discard_unknown_keys + self.disabled_client_side_validations = disabled_client_side_validations + self.logger = {} + """Logging Settings + """ + self.logger["package_logger"] = logging.getLogger("x_auth_id_alias") + self.logger["urllib3_logger"] = logging.getLogger("urllib3") + self.logger_format = '%(asctime)s %(levelname)s %(message)s' + """Log format + """ + self.logger_stream_handler = None + """Log stream handler + """ + self.logger_file_handler = None + """Log file handler + """ + self.logger_file = None + """Debug file location + """ + self.debug = False + """Debug switch + """ + + self.verify_ssl = True + """SSL/TLS verification + Set this to false to skip verifying SSL certificate when calling API + from https server. + """ + self.ssl_ca_cert = None + """Set this to customize the certificate file to verify the peer. + """ + self.cert_file = None + """client certificate file + """ + self.key_file = None + """client key file + """ + self.assert_hostname = None + """Set this to True/False to enable/disable SSL hostname verification. + """ + + self.connection_pool_maxsize = multiprocessing.cpu_count() * 5 + """urllib3 connection pool's maximum number of connections saved + per pool. urllib3 uses 1 connection as default value, but this is + not the best value when you are making a lot of possibly parallel + requests to the same host, which is often the case here. + cpu_count * 5 is used as default value to increase performance. + """ + + self.proxy = None + """Proxy URL + """ + self.proxy_headers = None + """Proxy headers + """ + self.safe_chars_for_path_param = '' + """Safe chars for path_param + """ + self.retries = None + """Adding retries to override urllib3 default value 3 + """ + # Disable client side validation + self.client_side_validation = True + + def __deepcopy__(self, memo): + cls = self.__class__ + result = cls.__new__(cls) + memo[id(self)] = result + for k, v in self.__dict__.items(): + if k not in ('logger', 'logger_file_handler'): + setattr(result, k, copy.deepcopy(v, memo)) + # shallow copy of loggers + result.logger = copy.copy(self.logger) + # use setters to configure loggers + result.logger_file = self.logger_file + result.debug = self.debug + return result + + def __setattr__(self, name, value): + object.__setattr__(self, name, value) + if name == 'disabled_client_side_validations': + s = set(filter(None, value.split(','))) + for v in s: + if v not in JSON_SCHEMA_VALIDATION_KEYWORDS: + raise ApiValueError( + "Invalid keyword: '{0}''".format(v)) + self._disabled_client_side_validations = s + + @classmethod + def set_default(cls, default): + """Set default instance of configuration. + + It stores default configuration, which can be + returned by get_default_copy method. + + :param default: object of Configuration + """ + cls._default = copy.deepcopy(default) + + @classmethod + def get_default_copy(cls): + """Return new instance of configuration. + + This method returns newly created, based on default constructor, + object of Configuration class or returns a copy of default + configuration passed by the set_default method. + + :return: The configuration object. + """ + if cls._default is not None: + return copy.deepcopy(cls._default) + return Configuration() + + @property + def logger_file(self): + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + return self.__logger_file + + @logger_file.setter + def logger_file(self, value): + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + self.__logger_file = value + if self.__logger_file: + # If set logging file, + # then add file handler and remove stream handler. + self.logger_file_handler = logging.FileHandler(self.__logger_file) + self.logger_file_handler.setFormatter(self.logger_formatter) + for _, logger in six.iteritems(self.logger): + logger.addHandler(self.logger_file_handler) + + @property + def debug(self): + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + return self.__debug + + @debug.setter + def debug(self, value): + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + self.__debug = value + if self.__debug: + # if debug status is True, turn on debug logging + for _, logger in six.iteritems(self.logger): + logger.setLevel(logging.DEBUG) + # turn on httplib debug + httplib.HTTPConnection.debuglevel = 1 + else: + # if debug status is False, turn off debug logging, + # setting log level to default `logging.WARNING` + for _, logger in six.iteritems(self.logger): + logger.setLevel(logging.WARNING) + # turn off httplib debug + httplib.HTTPConnection.debuglevel = 0 + + @property + def logger_format(self): + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + return self.__logger_format + + @logger_format.setter + def logger_format(self, value): + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + self.__logger_format = value + self.logger_formatter = logging.Formatter(self.__logger_format) + + def get_api_key_with_prefix(self, identifier, alias=None): + """Gets API key (with prefix if set). + + :param identifier: The identifier of apiKey. + :param alias: The alternative identifier of apiKey. + :return: The token for api key authentication. + """ + if self.refresh_api_key_hook is not None: + self.refresh_api_key_hook(self) + key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) + if key: + prefix = self.api_key_prefix.get(identifier) + if prefix: + return "%s %s" % (prefix, key) + else: + return key + + def get_basic_auth_token(self): + """Gets HTTP basic authentication header (string). + + :return: The token for basic HTTP authentication. + """ + username = "" + if self.username is not None: + username = self.username + password = "" + if self.password is not None: + password = self.password + return urllib3.util.make_headers( + basic_auth=username + ':' + password + ).get('authorization') + + def auth_settings(self): + """Gets Auth Settings dict for api client. + + :return: The Auth Settings information dict. + """ + auth = {} + if 'api_key' in self.api_key: + auth['api_key'] = { + 'type': 'api_key', + 'in': 'header', + 'key': 'X-Api-Key', + 'value': self.get_api_key_with_prefix( + 'api_key', + ), + } + if 'api_key_query' in self.api_key or 'api_key' in self.api_key: + auth['api_key_query'] = { + 'type': 'api_key', + 'in': 'query', + 'key': 'api_key', + 'value': self.get_api_key_with_prefix( + 'api_key_query', + alias='api_key', + ), + } + return auth + + def to_debug_report(self): + """Gets the essential information for debugging. + + :return: The report for debugging. + """ + return "Python SDK Debug Report:\n"\ + "OS: {env}\n"\ + "Python Version: {pyversion}\n"\ + "Version of the API: 1.0.0\n"\ + "SDK Package Version: 1.0.0".\ + format(env=sys.platform, pyversion=sys.version) + + def get_host_settings(self): + """Gets an array of host settings + + :return: An array of host settings + """ + return [ + { + 'url': "http://{server}.swagger.io:{port}/v2", + 'description': "petstore server", + 'variables': { + 'server': { + 'description': "No description provided", + 'default_value': "petstore", + 'enum_values': [ + "petstore", + "qa-petstore", + "dev-petstore" + ] + }, + 'port': { + 'description': "No description provided", + 'default_value': "80", + 'enum_values': [ + "80", + "8080" + ] + } + } + }, + { + 'url': "https://localhost:8080/{version}", + 'description': "The local server", + 'variables': { + 'version': { + 'description': "No description provided", + 'default_value': "v2", + 'enum_values': [ + "v1", + "v2" + ] + } + } + } + ] + + def get_host_from_settings(self, index, variables=None): + """Gets host URL based on the index and variables + :param index: array index of the host settings + :param variables: hash of variable and the corresponding value + :return: URL based on host settings + """ + variables = {} if variables is None else variables + servers = self.get_host_settings() + + try: + server = servers[index] + except IndexError: + raise ValueError( + "Invalid index {0} when selecting the host settings. " + "Must be less than {1}".format(index, len(servers))) + + url = server['url'] + + # go through variables and replace placeholders + for variable_name, variable in server['variables'].items(): + used_value = variables.get( + variable_name, variable['default_value']) + + if 'enum_values' in variable \ + and used_value not in variable['enum_values']: + raise ValueError( + "The variable `{0}` in the host URL has invalid value " + "{1}. Must be {2}.".format( + variable_name, variables[variable_name], + variable['enum_values'])) + + url = url.replace("{" + variable_name + "}", used_value) + + return url diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/exceptions.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/exceptions.py new file mode 100644 index 000000000000..13f8852a90fa --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/exceptions.py @@ -0,0 +1,139 @@ +# coding: utf-8 + +""" + OpenAPI Extension x-auth-id-alias + + This specification shows how to use x-auth-id-alias extension for API keys. # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +import six + + +class OpenApiException(Exception): + """The base exception class for all OpenAPIExceptions""" + + +class ApiTypeError(OpenApiException, TypeError): + def __init__(self, msg, path_to_item=None, valid_classes=None, + key_type=None): + """ Raises an exception for TypeErrors + + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list): a list of keys an indices to get to the + current_item + None if unset + valid_classes (tuple): the primitive classes that current item + should be an instance of + None if unset + key_type (bool): False if our value is a value in a dict + True if it is a key in a dict + False if our item is an item in a list + None if unset + """ + self.path_to_item = path_to_item + self.valid_classes = valid_classes + self.key_type = key_type + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiTypeError, self).__init__(full_msg) + + +class ApiValueError(OpenApiException, ValueError): + def __init__(self, msg, path_to_item=None): + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list) the path to the exception in the + received_data dict. None if unset + """ + + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiValueError, self).__init__(full_msg) + + +class ApiAttributeError(OpenApiException, AttributeError): + def __init__(self, msg, path_to_item=None): + """ + Raised when an attribute reference or assignment fails. + + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (None/list) the path to the exception in the + received_data dict + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiAttributeError, self).__init__(full_msg) + + +class ApiKeyError(OpenApiException, KeyError): + def __init__(self, msg, path_to_item=None): + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (None/list) the path to the exception in the + received_data dict + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiKeyError, self).__init__(full_msg) + + +class ApiException(OpenApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + if http_resp: + self.status = http_resp.status + self.reason = http_resp.reason + self.body = http_resp.data + self.headers = http_resp.getheaders() + else: + self.status = status + self.reason = reason + self.body = None + self.headers = None + + def __str__(self): + """Custom error messages for exception""" + error_message = "({0})\n"\ + "Reason: {1}\n".format(self.status, self.reason) + if self.headers: + error_message += "HTTP response headers: {0}\n".format( + self.headers) + + if self.body: + error_message += "HTTP response body: {0}\n".format(self.body) + + return error_message + + +def render_path(path_to_item): + """Returns a string representation of a path""" + result = "" + for pth in path_to_item: + if isinstance(pth, six.integer_types): + result += "[{0}]".format(pth) + else: + result += "['{0}']".format(pth) + return result diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/model/__init__.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/model/__init__.py new file mode 100644 index 000000000000..cfe32b784926 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/model/__init__.py @@ -0,0 +1,5 @@ +# we can not import model classes here because that would create a circular +# reference which would not work in python2 +# do not import all models into this module because that uses a lot of memory and stack frames +# if you need the ability to import all models from one package, import them with +# from {{packageName}.models import ModelA, ModelB diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/model_utils.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/model_utils.py new file mode 100644 index 000000000000..7e90316078fa --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/model_utils.py @@ -0,0 +1,1770 @@ +# coding: utf-8 + +""" + OpenAPI Extension x-auth-id-alias + + This specification shows how to use x-auth-id-alias extension for API keys. # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +from datetime import date, datetime # noqa: F401 +import inspect +import os +import pprint +import re +import tempfile + +from dateutil.parser import parse +import six + +from x_auth_id_alias.exceptions import ( + ApiKeyError, + ApiAttributeError, + ApiTypeError, + ApiValueError, +) + +none_type = type(None) +if six.PY3: + import io + file_type = io.IOBase + # these are needed for when other modules import str and int from here + str = str + int = int +else: + file_type = file # noqa: F821 + str_py2 = str + unicode_py2 = unicode # noqa: F821 + long_py2 = long # noqa: F821 + int_py2 = int + # this requires that the future library is installed + from builtins import int, str + + +class cached_property(object): + # this caches the result of the function call for fn with no inputs + # use this as a decorator on fuction methods that you want converted + # into cached properties + result_key = '_results' + + def __init__(self, fn): + self._fn = fn + + def __get__(self, instance, cls=None): + try: + return vars(self)[self.result_key] + except KeyError: + result = self._fn() + setattr(self, self.result_key, result) + return result + + +class OpenApiModel(object): + """The base class for all OpenAPIModels""" + + def set_attribute(self, name, value): + # this is only used to set properties on self + + path_to_item = [] + if self._path_to_item: + path_to_item.extend(self._path_to_item) + path_to_item.append(name) + + if name in self.openapi_types: + required_types_mixed = self.openapi_types[name] + elif self.additional_properties_type is None: + raise ApiAttributeError( + "{0} has no attribute '{1}'".format( + type(self).__name__, name), + path_to_item + ) + elif self.additional_properties_type is not None: + required_types_mixed = self.additional_properties_type + + if get_simple_class(name) != str: + error_msg = type_error_message( + var_name=name, + var_value=name, + valid_classes=(str,), + key_type=True + ) + raise ApiTypeError( + error_msg, + path_to_item=path_to_item, + valid_classes=(str,), + key_type=True + ) + + if self._check_type: + value = validate_and_convert_types( + value, required_types_mixed, path_to_item, self._spec_property_naming, + self._check_type, configuration=self._configuration) + if (name,) in self.allowed_values: + check_allowed_values( + self.allowed_values, + (name,), + value + ) + if (name,) in self.validations: + check_validations( + self.validations, + (name,), + value, + self._configuration + ) + self.__dict__['_data_store'][name] = value + + def __setitem__(self, name, value): + """this allows us to set values with instance[field_name] = val""" + self.__setattr__(name, value) + + def __getitem__(self, name): + """this allows us to get a value with val = instance[field_name]""" + return self.__getattr__(name) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other + + def __new__(cls, *args, **kwargs): + # this function uses the discriminator to + # pick a new schema/class to instantiate because a discriminator + # propertyName value was passed in + + if len(args) == 1 and args[0] is None and is_type_nullable(cls): + # The input data is the 'null' value and the type is nullable. + return None + + visited_composed_classes = kwargs.get('_visited_composed_classes', ()) + if ( + cls.discriminator is None or + cls in visited_composed_classes + ): + # Use case 1: this openapi schema (cls) does not have a discriminator + # Use case 2: we have already visited this class before and are sure that we + # want to instantiate it this time. We have visited this class deserializing + # a payload with a discriminator. During that process we traveled through + # this class but did not make an instance of it. Now we are making an + # instance of a composed class which contains cls in it, so this time make an instance of cls. + # + # Here's an example of use case 2: If Animal has a discriminator + # petType and we pass in "Dog", and the class Dog + # allOf includes Animal, we move through Animal + # once using the discriminator, and pick Dog. + # Then in the composed schema dog Dog, we will make an instance of the + # Animal class (because Dal has allOf: Animal) but this time we won't travel + # through Animal's discriminator because we passed in + # _visited_composed_classes = (Animal,) + + return super(OpenApiModel, cls).__new__(cls) + + # Get the name and value of the discriminator property. + # The discriminator name is obtained from the discriminator meta-data + # and the discriminator value is obtained from the input data. + discr_propertyname_py = list(cls.discriminator.keys())[0] + discr_propertyname_js = cls.attribute_map[discr_propertyname_py] + if discr_propertyname_js in kwargs: + discr_value = kwargs[discr_propertyname_js] + elif discr_propertyname_py in kwargs: + discr_value = kwargs[discr_propertyname_py] + else: + # The input data does not contain the discriminator property. + path_to_item = kwargs.get('_path_to_item', ()) + raise ApiValueError( + "Cannot deserialize input data due to missing discriminator. " + "The discriminator property '%s' is missing at path: %s" % + (discr_propertyname_js, path_to_item) + ) + + # Implementation note: the last argument to get_discriminator_class + # is a list of visited classes. get_discriminator_class may recursively + # call itself and update the list of visited classes, and the initial + # value must be an empty list. Hence not using 'visited_composed_classes' + new_cls = get_discriminator_class( + cls, discr_propertyname_py, discr_value, []) + if new_cls is None: + path_to_item = kwargs.get('_path_to_item', ()) + disc_prop_value = kwargs.get( + discr_propertyname_js, kwargs.get(discr_propertyname_py)) + raise ApiValueError( + "Cannot deserialize input data due to invalid discriminator " + "value. The OpenAPI document has no mapping for discriminator " + "property '%s'='%s' at path: %s" % + (discr_propertyname_js, disc_prop_value, path_to_item) + ) + + if new_cls in visited_composed_classes: + # if we are making an instance of a composed schema Descendent + # which allOf includes Ancestor, then Ancestor contains + # a discriminator that includes Descendent. + # So if we make an instance of Descendent, we have to make an + # instance of Ancestor to hold the allOf properties. + # This code detects that use case and makes the instance of Ancestor + # For example: + # When making an instance of Dog, _visited_composed_classes = (Dog,) + # then we make an instance of Animal to include in dog._composed_instances + # so when we are here, cls is Animal + # cls.discriminator != None + # cls not in _visited_composed_classes + # new_cls = Dog + # but we know we know that we already have Dog + # because it is in visited_composed_classes + # so make Animal here + return super(OpenApiModel, cls).__new__(cls) + + # Build a list containing all oneOf and anyOf descendants. + oneof_anyof_classes = None + if cls._composed_schemas is not None: + oneof_anyof_classes = ( + cls._composed_schemas.get('oneOf', ()) + + cls._composed_schemas.get('anyOf', ())) + oneof_anyof_child = new_cls in oneof_anyof_classes + kwargs['_visited_composed_classes'] = visited_composed_classes + (cls,) + + if cls._composed_schemas.get('allOf') and oneof_anyof_child: + # Validate that we can make self because when we make the + # new_cls it will not include the allOf validations in self + self_inst = super(OpenApiModel, cls).__new__(cls) + self_inst.__init__(*args, **kwargs) + + new_inst = new_cls.__new__(new_cls, *args, **kwargs) + new_inst.__init__(*args, **kwargs) + return new_inst + + +class ModelSimple(OpenApiModel): + """the parent class of models whose type != object in their + swagger/openapi""" + + def __setattr__(self, name, value): + """this allows us to set a value with instance.field_name = val""" + if name in self.required_properties: + self.__dict__[name] = value + return + + self.set_attribute(name, value) + + def __getattr__(self, name): + """this allows us to get a value with val = instance.field_name""" + if name in self.required_properties: + return self.__dict__[name] + + if name in self.__dict__['_data_store']: + return self.__dict__['_data_store'][name] + + path_to_item = [] + if self._path_to_item: + path_to_item.extend(self._path_to_item) + path_to_item.append(name) + raise ApiAttributeError( + "{0} has no attribute '{1}'".format( + type(self).__name__, name), + [name] + ) + + def to_str(self): + """Returns the string representation of the model""" + return str(self.value) + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, self.__class__): + return False + + this_val = self._data_store['value'] + that_val = other._data_store['value'] + types = set() + types.add(this_val.__class__) + types.add(that_val.__class__) + vals_equal = this_val == that_val + if not six.PY3 and len(types) == 2 and unicode in types: # noqa: F821 + vals_equal = ( + this_val.encode('utf-8') == that_val.encode('utf-8') + ) + if not vals_equal: + return False + return True + + +class ModelNormal(OpenApiModel): + """the parent class of models whose type == object in their + swagger/openapi""" + + def __setattr__(self, name, value): + """this allows us to set a value with instance.field_name = val""" + if name in self.required_properties: + self.__dict__[name] = value + return + + self.set_attribute(name, value) + + def __getattr__(self, name): + """this allows us to get a value with val = instance.field_name""" + if name in self.required_properties: + return self.__dict__[name] + + if name in self.__dict__['_data_store']: + return self.__dict__['_data_store'][name] + + path_to_item = [] + if self._path_to_item: + path_to_item.extend(self._path_to_item) + path_to_item.append(name) + raise ApiAttributeError( + "{0} has no attribute '{1}'".format( + type(self).__name__, name), + [name] + ) + + def to_dict(self): + """Returns the model properties as a dict""" + return model_to_dict(self, serialize=False) + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, self.__class__): + return False + + if not set(self._data_store.keys()) == set(other._data_store.keys()): + return False + for _var_name, this_val in six.iteritems(self._data_store): + that_val = other._data_store[_var_name] + types = set() + types.add(this_val.__class__) + types.add(that_val.__class__) + vals_equal = this_val == that_val + if (not six.PY3 and + len(types) == 2 and unicode in types): # noqa: F821 + vals_equal = ( + this_val.encode('utf-8') == that_val.encode('utf-8') + ) + if not vals_equal: + return False + return True + + +class ModelComposed(OpenApiModel): + """the parent class of models whose type == object in their + swagger/openapi and have oneOf/allOf/anyOf + + When one sets a property we use var_name_to_model_instances to store the value in + the correct class instances + run any type checking + validation code. + When one gets a property we use var_name_to_model_instances to get the value + from the correct class instances. + This allows multiple composed schemas to contain the same property with additive + constraints on the value. + + _composed_schemas (dict) stores the anyOf/allOf/oneOf classes + key (str): allOf/oneOf/anyOf + value (list): the classes in the XOf definition. + Note: none_type can be included when the openapi document version >= 3.1.0 + _composed_instances (list): stores a list of instances of the composed schemas + defined in _composed_schemas. When properties are accessed in the self instance, + they are returned from the self._data_store or the data stores in the instances + in self._composed_schemas + _var_name_to_model_instances (dict): maps between a variable name on self and + the composed instances (self included) which contain that data + key (str): property name + value (list): list of class instances, self or instances in _composed_instances + which contain the value that the key is referring to. + """ + + def __setattr__(self, name, value): + """this allows us to set a value with instance.field_name = val""" + if name in self.required_properties: + self.__dict__[name] = value + return + + # set the attribute on the correct instance + model_instances = self._var_name_to_model_instances.get( + name, self._additional_properties_model_instances) + if model_instances: + for model_instance in model_instances: + if model_instance == self: + self.set_attribute(name, value) + else: + setattr(model_instance, name, value) + if name not in self._var_name_to_model_instances: + # we assigned an additional property + self.__dict__['_var_name_to_model_instances'][name] = ( + model_instance + ) + return None + + path_to_item = [] + if self._path_to_item: + path_to_item.extend(self._path_to_item) + path_to_item.append(name) + raise ApiAttributeError( + "{0} has no attribute '{1}'".format( + type(self).__name__, name), + path_to_item + ) + + def __getattr__(self, name): + """this allows us to get a value with val = instance.field_name""" + if name in self.required_properties: + return self.__dict__[name] + + # get the attribute from the correct instance + model_instances = self._var_name_to_model_instances.get( + name, self._additional_properties_model_instances) + path_to_item = [] + if self._path_to_item: + path_to_item.extend(self._path_to_item) + path_to_item.append(name) + values = [] + # A composed model stores child (oneof/anyOf/allOf) models under + # self._var_name_to_model_instances. A named property can exist in + # multiple child models. If the property is present in more than one + # child model, the value must be the same across all the child models. + if model_instances: + for model_instance in model_instances: + if name in model_instance._data_store: + v = model_instance._data_store[name] + if v not in values: + values.append(v) + len_values = len(values) + if len_values == 0: + raise ApiAttributeError( + "{0} has no attribute '{1}'".format( + type(self).__name__, name), + path_to_item + ) + elif len_values == 1: + return values[0] + elif len_values > 1: + raise ApiValueError( + "Values stored for property {0} in {1} differ when looking " + "at self and self's composed instances. All values must be " + "the same".format(name, type(self).__name__), + path_to_item + ) + + def to_dict(self): + """Returns the model properties as a dict""" + return model_to_dict(self, serialize=False) + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, self.__class__): + return False + + if not set(self._data_store.keys()) == set(other._data_store.keys()): + return False + for _var_name, this_val in six.iteritems(self._data_store): + that_val = other._data_store[_var_name] + types = set() + types.add(this_val.__class__) + types.add(that_val.__class__) + vals_equal = this_val == that_val + if (not six.PY3 and + len(types) == 2 and unicode in types): # noqa: F821 + vals_equal = ( + this_val.encode('utf-8') == that_val.encode('utf-8') + ) + if not vals_equal: + return False + return True + + +COERCION_INDEX_BY_TYPE = { + ModelComposed: 0, + ModelNormal: 1, + ModelSimple: 2, + none_type: 3, # The type of 'None'. + list: 4, + dict: 5, + float: 6, + int: 7, + bool: 8, + datetime: 9, + date: 10, + str: 11, + file_type: 12, # 'file_type' is an alias for the built-in 'file' or 'io.IOBase' type. +} + +# these are used to limit what type conversions we try to do +# when we have a valid type already and we want to try converting +# to another type +UPCONVERSION_TYPE_PAIRS = ( + (str, datetime), + (str, date), + (int, float), # A float may be serialized as an integer, e.g. '3' is a valid serialized float. + (list, ModelComposed), + (dict, ModelComposed), + (list, ModelNormal), + (dict, ModelNormal), + (str, ModelSimple), + (int, ModelSimple), + (float, ModelSimple), + (list, ModelSimple), +) + +COERCIBLE_TYPE_PAIRS = { + False: ( # client instantiation of a model with client data + # (dict, ModelComposed), + # (list, ModelComposed), + # (dict, ModelNormal), + # (list, ModelNormal), + # (str, ModelSimple), + # (int, ModelSimple), + # (float, ModelSimple), + # (list, ModelSimple), + # (str, int), + # (str, float), + # (str, datetime), + # (str, date), + # (int, str), + # (float, str), + ), + True: ( # server -> client data + (dict, ModelComposed), + (list, ModelComposed), + (dict, ModelNormal), + (list, ModelNormal), + (str, ModelSimple), + (int, ModelSimple), + (float, ModelSimple), + (list, ModelSimple), + # (str, int), + # (str, float), + (str, datetime), + (str, date), + # (int, str), + # (float, str), + (str, file_type) + ), +} + + +def get_simple_class(input_value): + """Returns an input_value's simple class that we will use for type checking + Python2: + float and int will return int, where int is the python3 int backport + str and unicode will return str, where str is the python3 str backport + Note: float and int ARE both instances of int backport + Note: str_py2 and unicode_py2 are NOT both instances of str backport + + Args: + input_value (class/class_instance): the item for which we will return + the simple class + """ + if isinstance(input_value, type): + # input_value is a class + return input_value + elif isinstance(input_value, tuple): + return tuple + elif isinstance(input_value, list): + return list + elif isinstance(input_value, dict): + return dict + elif isinstance(input_value, none_type): + return none_type + elif isinstance(input_value, file_type): + return file_type + elif isinstance(input_value, bool): + # this must be higher than the int check because + # isinstance(True, int) == True + return bool + elif isinstance(input_value, int): + # for python2 input_value==long_instance -> return int + # where int is the python3 int backport + return int + elif isinstance(input_value, datetime): + # this must be higher than the date check because + # isinstance(datetime_instance, date) == True + return datetime + elif isinstance(input_value, date): + return date + elif (six.PY2 and isinstance(input_value, (str_py2, unicode_py2, str)) or + isinstance(input_value, str)): + return str + return type(input_value) + + +def check_allowed_values(allowed_values, input_variable_path, input_values): + """Raises an exception if the input_values are not allowed + + Args: + allowed_values (dict): the allowed_values dict + input_variable_path (tuple): the path to the input variable + input_values (list/str/int/float/date/datetime): the values that we + are checking to see if they are in allowed_values + """ + these_allowed_values = list(allowed_values[input_variable_path].values()) + if (isinstance(input_values, list) + and not set(input_values).issubset( + set(these_allowed_values))): + invalid_values = ", ".join( + map(str, set(input_values) - set(these_allowed_values))), + raise ApiValueError( + "Invalid values for `%s` [%s], must be a subset of [%s]" % + ( + input_variable_path[0], + invalid_values, + ", ".join(map(str, these_allowed_values)) + ) + ) + elif (isinstance(input_values, dict) + and not set( + input_values.keys()).issubset(set(these_allowed_values))): + invalid_values = ", ".join( + map(str, set(input_values.keys()) - set(these_allowed_values))) + raise ApiValueError( + "Invalid keys in `%s` [%s], must be a subset of [%s]" % + ( + input_variable_path[0], + invalid_values, + ", ".join(map(str, these_allowed_values)) + ) + ) + elif (not isinstance(input_values, (list, dict)) + and input_values not in these_allowed_values): + raise ApiValueError( + "Invalid value for `%s` (%s), must be one of %s" % + ( + input_variable_path[0], + input_values, + these_allowed_values + ) + ) + + +def is_json_validation_enabled(schema_keyword, configuration=None): + """Returns true if JSON schema validation is enabled for the specified + validation keyword. This can be used to skip JSON schema structural validation + as requested in the configuration. + + Args: + schema_keyword (string): the name of a JSON schema validation keyword. + configuration (Configuration): the configuration class. + """ + + return (configuration is None or + not hasattr(configuration, '_disabled_client_side_validations') or + schema_keyword not in configuration._disabled_client_side_validations) + + +def check_validations( + validations, input_variable_path, input_values, + configuration=None): + """Raises an exception if the input_values are invalid + + Args: + validations (dict): the validation dictionary. + input_variable_path (tuple): the path to the input variable. + input_values (list/str/int/float/date/datetime): the values that we + are checking. + configuration (Configuration): the configuration class. + """ + + current_validations = validations[input_variable_path] + if (is_json_validation_enabled('multipleOf', configuration) and + 'multiple_of' in current_validations and + isinstance(input_values, (int, float)) and + not (float(input_values) / current_validations['multiple_of']).is_integer()): + # Note 'multipleOf' will be as good as the floating point arithmetic. + raise ApiValueError( + "Invalid value for `%s`, value must be a multiple of " + "`%s`" % ( + input_variable_path[0], + current_validations['multiple_of'] + ) + ) + + if (is_json_validation_enabled('maxLength', configuration) and + 'max_length' in current_validations and + len(input_values) > current_validations['max_length']): + raise ApiValueError( + "Invalid value for `%s`, length must be less than or equal to " + "`%s`" % ( + input_variable_path[0], + current_validations['max_length'] + ) + ) + + if (is_json_validation_enabled('minLength', configuration) and + 'min_length' in current_validations and + len(input_values) < current_validations['min_length']): + raise ApiValueError( + "Invalid value for `%s`, length must be greater than or equal to " + "`%s`" % ( + input_variable_path[0], + current_validations['min_length'] + ) + ) + + if (is_json_validation_enabled('maxItems', configuration) and + 'max_items' in current_validations and + len(input_values) > current_validations['max_items']): + raise ApiValueError( + "Invalid value for `%s`, number of items must be less than or " + "equal to `%s`" % ( + input_variable_path[0], + current_validations['max_items'] + ) + ) + + if (is_json_validation_enabled('minItems', configuration) and + 'min_items' in current_validations and + len(input_values) < current_validations['min_items']): + raise ValueError( + "Invalid value for `%s`, number of items must be greater than or " + "equal to `%s`" % ( + input_variable_path[0], + current_validations['min_items'] + ) + ) + + items = ('exclusive_maximum', 'inclusive_maximum', 'exclusive_minimum', + 'inclusive_minimum') + if (any(item in current_validations for item in items)): + if isinstance(input_values, list): + max_val = max(input_values) + min_val = min(input_values) + elif isinstance(input_values, dict): + max_val = max(input_values.values()) + min_val = min(input_values.values()) + else: + max_val = input_values + min_val = input_values + + if (is_json_validation_enabled('exclusiveMaximum', configuration) and + 'exclusive_maximum' in current_validations and + max_val >= current_validations['exclusive_maximum']): + raise ApiValueError( + "Invalid value for `%s`, must be a value less than `%s`" % ( + input_variable_path[0], + current_validations['exclusive_maximum'] + ) + ) + + if (is_json_validation_enabled('maximum', configuration) and + 'inclusive_maximum' in current_validations and + max_val > current_validations['inclusive_maximum']): + raise ApiValueError( + "Invalid value for `%s`, must be a value less than or equal to " + "`%s`" % ( + input_variable_path[0], + current_validations['inclusive_maximum'] + ) + ) + + if (is_json_validation_enabled('exclusiveMinimum', configuration) and + 'exclusive_minimum' in current_validations and + min_val <= current_validations['exclusive_minimum']): + raise ApiValueError( + "Invalid value for `%s`, must be a value greater than `%s`" % + ( + input_variable_path[0], + current_validations['exclusive_maximum'] + ) + ) + + if (is_json_validation_enabled('minimum', configuration) and + 'inclusive_minimum' in current_validations and + min_val < current_validations['inclusive_minimum']): + raise ApiValueError( + "Invalid value for `%s`, must be a value greater than or equal " + "to `%s`" % ( + input_variable_path[0], + current_validations['inclusive_minimum'] + ) + ) + flags = current_validations.get('regex', {}).get('flags', 0) + if (is_json_validation_enabled('pattern', configuration) and + 'regex' in current_validations and + not re.search(current_validations['regex']['pattern'], + input_values, flags=flags)): + err_msg = r"Invalid value for `%s`, must match regular expression `%s`" % ( + input_variable_path[0], + current_validations['regex']['pattern'] + ) + if flags != 0: + # Don't print the regex flags if the flags are not + # specified in the OAS document. + err_msg = r"%s with flags=`%s`" % (err_msg, flags) + raise ApiValueError(err_msg) + + +def order_response_types(required_types): + """Returns the required types sorted in coercion order + + Args: + required_types (list/tuple): collection of classes or instance of + list or dict with class information inside it. + + Returns: + (list): coercion order sorted collection of classes or instance + of list or dict with class information inside it. + """ + + def index_getter(class_or_instance): + if isinstance(class_or_instance, list): + return COERCION_INDEX_BY_TYPE[list] + elif isinstance(class_or_instance, dict): + return COERCION_INDEX_BY_TYPE[dict] + elif (inspect.isclass(class_or_instance) + and issubclass(class_or_instance, ModelComposed)): + return COERCION_INDEX_BY_TYPE[ModelComposed] + elif (inspect.isclass(class_or_instance) + and issubclass(class_or_instance, ModelNormal)): + return COERCION_INDEX_BY_TYPE[ModelNormal] + elif (inspect.isclass(class_or_instance) + and issubclass(class_or_instance, ModelSimple)): + return COERCION_INDEX_BY_TYPE[ModelSimple] + elif class_or_instance in COERCION_INDEX_BY_TYPE: + return COERCION_INDEX_BY_TYPE[class_or_instance] + raise ApiValueError("Unsupported type: %s" % class_or_instance) + + sorted_types = sorted( + required_types, + key=lambda class_or_instance: index_getter(class_or_instance) + ) + return sorted_types + + +def remove_uncoercible(required_types_classes, current_item, spec_property_naming, + must_convert=True): + """Only keeps the type conversions that are possible + + Args: + required_types_classes (tuple): tuple of classes that are required + these should be ordered by COERCION_INDEX_BY_TYPE + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + current_item (any): the current item (input data) to be converted + + Keyword Args: + must_convert (bool): if True the item to convert is of the wrong + type and we want a big list of coercibles + if False, we want a limited list of coercibles + + Returns: + (list): the remaining coercible required types, classes only + """ + current_type_simple = get_simple_class(current_item) + + results_classes = [] + for required_type_class in required_types_classes: + # convert our models to OpenApiModel + required_type_class_simplified = required_type_class + if isinstance(required_type_class_simplified, type): + if issubclass(required_type_class_simplified, ModelComposed): + required_type_class_simplified = ModelComposed + elif issubclass(required_type_class_simplified, ModelNormal): + required_type_class_simplified = ModelNormal + elif issubclass(required_type_class_simplified, ModelSimple): + required_type_class_simplified = ModelSimple + + if required_type_class_simplified == current_type_simple: + # don't consider converting to one's own class + continue + + class_pair = (current_type_simple, required_type_class_simplified) + if must_convert and class_pair in COERCIBLE_TYPE_PAIRS[spec_property_naming]: + results_classes.append(required_type_class) + elif class_pair in UPCONVERSION_TYPE_PAIRS: + results_classes.append(required_type_class) + return results_classes + + +def get_required_type_classes(required_types_mixed): + """Converts the tuple required_types into a tuple and a dict described + below + + Args: + required_types_mixed (tuple/list): will contain either classes or + instance of list or dict + + Returns: + (valid_classes, dict_valid_class_to_child_types_mixed): + valid_classes (tuple): the valid classes that the current item + should be + dict_valid_class_to_child_types_mixed (doct): + valid_class (class): this is the key + child_types_mixed (list/dict/tuple): describes the valid child + types + """ + valid_classes = [] + child_req_types_by_current_type = {} + for required_type in required_types_mixed: + if isinstance(required_type, list): + valid_classes.append(list) + child_req_types_by_current_type[list] = required_type + elif isinstance(required_type, tuple): + valid_classes.append(tuple) + child_req_types_by_current_type[tuple] = required_type + elif isinstance(required_type, dict): + valid_classes.append(dict) + child_req_types_by_current_type[dict] = required_type[str] + else: + valid_classes.append(required_type) + return tuple(valid_classes), child_req_types_by_current_type + + +def change_keys_js_to_python(input_dict, model_class): + """ + Converts from javascript_key keys in the input_dict to python_keys in + the output dict using the mapping in model_class. + If the input_dict contains a key which does not declared in the model_class, + the key is added to the output dict as is. The assumption is the model_class + may have undeclared properties (additionalProperties attribute in the OAS + document). + """ + + if getattr(model_class, 'attribute_map', None) is None: + return input_dict + output_dict = {} + reversed_attr_map = {value: key for key, value in + model_class.attribute_map.items()} + for javascript_key, value in input_dict.items(): + python_key = reversed_attr_map.get(javascript_key) + if python_key is None: + # if the key is unknown, it is in error or it is an + # additionalProperties variable + python_key = javascript_key + output_dict[python_key] = value + return output_dict + + +def get_type_error(var_value, path_to_item, valid_classes, key_type=False): + error_msg = type_error_message( + var_name=path_to_item[-1], + var_value=var_value, + valid_classes=valid_classes, + key_type=key_type + ) + return ApiTypeError( + error_msg, + path_to_item=path_to_item, + valid_classes=valid_classes, + key_type=key_type + ) + + +def deserialize_primitive(data, klass, path_to_item): + """Deserializes string to primitive type. + + :param data: str/int/float + :param klass: str/class the class to convert to + + :return: int, float, str, bool, date, datetime + """ + additional_message = "" + try: + if klass in {datetime, date}: + additional_message = ( + "If you need your parameter to have a fallback " + "string value, please set its type as `type: {}` in your " + "spec. That allows the value to be any type. " + ) + if klass == datetime: + if len(data) < 8: + raise ValueError("This is not a datetime") + # The string should be in iso8601 datetime format. + parsed_datetime = parse(data) + date_only = ( + parsed_datetime.hour == 0 and + parsed_datetime.minute == 0 and + parsed_datetime.second == 0 and + parsed_datetime.tzinfo is None and + 8 <= len(data) <= 10 + ) + if date_only: + raise ValueError("This is a date, not a datetime") + return parsed_datetime + elif klass == date: + if len(data) < 8: + raise ValueError("This is not a date") + return parse(data).date() + else: + converted_value = klass(data) + if isinstance(data, str) and klass == float: + if str(converted_value) != data: + # '7' -> 7.0 -> '7.0' != '7' + raise ValueError('This is not a float') + return converted_value + except (OverflowError, ValueError) as ex: + # parse can raise OverflowError + six.raise_from(ApiValueError( + "{0}Failed to parse {1} as {2}".format( + additional_message, repr(data), get_py3_class_name(klass) + ), + path_to_item=path_to_item + ), ex) + + +def get_discriminator_class(model_class, + discr_name, + discr_value, cls_visited): + """Returns the child class specified by the discriminator. + + Args: + model_class (OpenApiModel): the model class. + discr_name (string): the name of the discriminator property. + discr_value (any): the discriminator value. + cls_visited (list): list of model classes that have been visited. + Used to determine the discriminator class without + visiting circular references indefinitely. + + Returns: + used_model_class (class/None): the chosen child class that will be used + to deserialize the data, for example dog.Dog. + If a class is not found, None is returned. + """ + + if model_class in cls_visited: + # The class has already been visited and no suitable class was found. + return None + cls_visited.append(model_class) + used_model_class = None + if discr_name in model_class.discriminator: + class_name_to_discr_class = model_class.discriminator[discr_name] + used_model_class = class_name_to_discr_class.get(discr_value) + if used_model_class is None: + # We didn't find a discriminated class in class_name_to_discr_class. + # So look in the ancestor or descendant discriminators + # The discriminator mapping may exist in a descendant (anyOf, oneOf) + # or ancestor (allOf). + # Ancestor example: in the GrandparentAnimal -> ParentPet -> ChildCat + # hierarchy, the discriminator mappings may be defined at any level + # in the hierarchy. + # Descendant example: mammal -> whale/zebra/Pig -> BasquePig/DanishPig + # if we try to make BasquePig from mammal, we need to travel through + # the oneOf descendant discriminators to find BasquePig + descendant_classes = model_class._composed_schemas.get('oneOf', ()) + \ + model_class._composed_schemas.get('anyOf', ()) + ancestor_classes = model_class._composed_schemas.get('allOf', ()) + possible_classes = descendant_classes + ancestor_classes + for cls in possible_classes: + # Check if the schema has inherited discriminators. + if hasattr(cls, 'discriminator') and cls.discriminator is not None: + used_model_class = get_discriminator_class( + cls, discr_name, discr_value, cls_visited) + if used_model_class is not None: + return used_model_class + return used_model_class + + +def deserialize_model(model_data, model_class, path_to_item, check_type, + configuration, spec_property_naming): + """Deserializes model_data to model instance. + + Args: + model_data (list/dict): data to instantiate the model + model_class (OpenApiModel): the model class + path_to_item (list): path to the model in the received data + check_type (bool): whether to check the data tupe for the values in + the model + configuration (Configuration): the instance to use to convert files + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + + Returns: + model instance + + Raise: + ApiTypeError + ApiValueError + ApiKeyError + """ + + kw_args = dict(_check_type=check_type, + _path_to_item=path_to_item, + _configuration=configuration, + _spec_property_naming=spec_property_naming) + + if issubclass(model_class, ModelSimple): + instance = model_class(value=model_data, **kw_args) + return instance + if isinstance(model_data, list): + instance = model_class(*model_data, **kw_args) + if isinstance(model_data, dict): + kw_args.update(model_data) + instance = model_class(**kw_args) + return instance + + +def deserialize_file(response_data, configuration, content_disposition=None): + """Deserializes body to file + + Saves response body into a file in a temporary folder, + using the filename from the `Content-Disposition` header if provided. + + Args: + param response_data (str): the file data to write + configuration (Configuration): the instance to use to convert files + + Keyword Args: + content_disposition (str): the value of the Content-Disposition + header + + Returns: + (file_type): the deserialized file which is open + The user is responsible for closing and reading the file + """ + fd, path = tempfile.mkstemp(dir=configuration.temp_folder_path) + os.close(fd) + os.remove(path) + + if content_disposition: + filename = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', + content_disposition).group(1) + path = os.path.join(os.path.dirname(path), filename) + + with open(path, "wb") as f: + if six.PY3 and isinstance(response_data, str): + # in python3 change str to bytes so we can write it + response_data = response_data.encode('utf-8') + f.write(response_data) + + f = open(path, "rb") + return f + + +def attempt_convert_item(input_value, valid_classes, path_to_item, + configuration, spec_property_naming, key_type=False, + must_convert=False, check_type=True): + """ + Args: + input_value (any): the data to convert + valid_classes (any): the classes that are valid + path_to_item (list): the path to the item to convert + configuration (Configuration): the instance to use to convert files + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + key_type (bool): if True we need to convert a key type (not supported) + must_convert (bool): if True we must convert + check_type (bool): if True we check the type or the returned data in + ModelComposed/ModelNormal/ModelSimple instances + + Returns: + instance (any) the fixed item + + Raises: + ApiTypeError + ApiValueError + ApiKeyError + """ + valid_classes_ordered = order_response_types(valid_classes) + valid_classes_coercible = remove_uncoercible( + valid_classes_ordered, input_value, spec_property_naming) + if not valid_classes_coercible or key_type: + # we do not handle keytype errors, json will take care + # of this for us + if configuration is None or not configuration.discard_unknown_keys: + raise get_type_error(input_value, path_to_item, valid_classes, + key_type=key_type) + for valid_class in valid_classes_coercible: + try: + if issubclass(valid_class, OpenApiModel): + return deserialize_model(input_value, valid_class, + path_to_item, check_type, + configuration, spec_property_naming) + elif valid_class == file_type: + return deserialize_file(input_value, configuration) + return deserialize_primitive(input_value, valid_class, + path_to_item) + except (ApiTypeError, ApiValueError, ApiKeyError) as conversion_exc: + if must_convert: + raise conversion_exc + # if we have conversion errors when must_convert == False + # we ignore the exception and move on to the next class + continue + # we were unable to convert, must_convert == False + return input_value + + +def is_type_nullable(input_type): + """ + Returns true if None is an allowed value for the specified input_type. + + A type is nullable if at least one of the following conditions is true: + 1. The OAS 'nullable' attribute has been specified, + 1. The type is the 'null' type, + 1. The type is a anyOf/oneOf composed schema, and a child schema is + the 'null' type. + Args: + input_type (type): the class of the input_value that we are + checking + Returns: + bool + """ + if input_type is none_type: + return True + if issubclass(input_type, OpenApiModel) and input_type._nullable: + return True + if issubclass(input_type, ModelComposed): + # If oneOf/anyOf, check if the 'null' type is one of the allowed types. + for t in input_type._composed_schemas.get('oneOf', ()): + if is_type_nullable(t): return True + for t in input_type._composed_schemas.get('anyOf', ()): + if is_type_nullable(t): return True + return False + + +def is_valid_type(input_class_simple, valid_classes): + """ + Args: + input_class_simple (class): the class of the input_value that we are + checking + valid_classes (tuple): the valid classes that the current item + should be + Returns: + bool + """ + valid_type = input_class_simple in valid_classes + if not valid_type and ( + issubclass(input_class_simple, OpenApiModel) or + input_class_simple is none_type): + for valid_class in valid_classes: + if input_class_simple is none_type and is_type_nullable(valid_class): + # Schema is oneOf/anyOf and the 'null' type is one of the allowed types. + return True + if not (issubclass(valid_class, OpenApiModel) and valid_class.discriminator): + continue + discr_propertyname_py = list(valid_class.discriminator.keys())[0] + discriminator_classes = ( + valid_class.discriminator[discr_propertyname_py].values() + ) + valid_type = is_valid_type(input_class_simple, discriminator_classes) + if valid_type: + return True + return valid_type + + +def validate_and_convert_types(input_value, required_types_mixed, path_to_item, + spec_property_naming, _check_type, configuration=None): + """Raises a TypeError is there is a problem, otherwise returns value + + Args: + input_value (any): the data to validate/convert + required_types_mixed (list/dict/tuple): A list of + valid classes, or a list tuples of valid classes, or a dict where + the value is a tuple of value classes + path_to_item: (list) the path to the data being validated + this stores a list of keys or indices to get to the data being + validated + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + _check_type: (boolean) if true, type will be checked and conversion + will be attempted. + configuration: (Configuration): the configuration class to use + when converting file_type items. + If passed, conversion will be attempted when possible + If not passed, no conversions will be attempted and + exceptions will be raised + + Returns: + the correctly typed value + + Raises: + ApiTypeError + """ + results = get_required_type_classes(required_types_mixed) + valid_classes, child_req_types_by_current_type = results + + input_class_simple = get_simple_class(input_value) + valid_type = is_valid_type(input_class_simple, valid_classes) + if not valid_type: + if configuration: + # if input_value is not valid_type try to convert it + converted_instance = attempt_convert_item( + input_value, + valid_classes, + path_to_item, + configuration, + spec_property_naming, + key_type=False, + must_convert=True + ) + return converted_instance + else: + raise get_type_error(input_value, path_to_item, valid_classes, + key_type=False) + + # input_value's type is in valid_classes + if len(valid_classes) > 1 and configuration: + # there are valid classes which are not the current class + valid_classes_coercible = remove_uncoercible( + valid_classes, input_value, spec_property_naming, must_convert=False) + if valid_classes_coercible: + converted_instance = attempt_convert_item( + input_value, + valid_classes_coercible, + path_to_item, + configuration, + spec_property_naming, + key_type=False, + must_convert=False + ) + return converted_instance + + if child_req_types_by_current_type == {}: + # all types are of the required types and there are no more inner + # variables left to look at + return input_value + inner_required_types = child_req_types_by_current_type.get( + type(input_value) + ) + if inner_required_types is None: + # for this type, there are not more inner variables left to look at + return input_value + if isinstance(input_value, list): + if input_value == []: + # allow an empty list + return input_value + for index, inner_value in enumerate(input_value): + inner_path = list(path_to_item) + inner_path.append(index) + input_value[index] = validate_and_convert_types( + inner_value, + inner_required_types, + inner_path, + spec_property_naming, + _check_type, + configuration=configuration + ) + elif isinstance(input_value, dict): + if input_value == {}: + # allow an empty dict + return input_value + for inner_key, inner_val in six.iteritems(input_value): + inner_path = list(path_to_item) + inner_path.append(inner_key) + if get_simple_class(inner_key) != str: + raise get_type_error(inner_key, inner_path, valid_classes, + key_type=True) + input_value[inner_key] = validate_and_convert_types( + inner_val, + inner_required_types, + inner_path, + spec_property_naming, + _check_type, + configuration=configuration + ) + return input_value + + +def model_to_dict(model_instance, serialize=True): + """Returns the model properties as a dict + + Args: + model_instance (one of your model instances): the model instance that + will be converted to a dict. + + Keyword Args: + serialize (bool): if True, the keys in the dict will be values from + attribute_map + """ + result = {} + + model_instances = [model_instance] + if model_instance._composed_schemas: + model_instances.extend(model_instance._composed_instances) + for model_instance in model_instances: + for attr, value in six.iteritems(model_instance._data_store): + if serialize: + # we use get here because additional property key names do not + # exist in attribute_map + attr = model_instance.attribute_map.get(attr, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: model_to_dict(x, serialize=serialize) + if hasattr(x, '_data_store') else x, value + )) + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], + model_to_dict(item[1], serialize=serialize)) + if hasattr(item[1], '_data_store') else item, + value.items() + )) + elif hasattr(value, '_data_store'): + result[attr] = model_to_dict(value, serialize=serialize) + else: + result[attr] = value + + return result + + +def type_error_message(var_value=None, var_name=None, valid_classes=None, + key_type=None): + """ + Keyword Args: + var_value (any): the variable which has the type_error + var_name (str): the name of the variable which has the typ error + valid_classes (tuple): the accepted classes for current_item's + value + key_type (bool): False if our value is a value in a dict + True if it is a key in a dict + False if our item is an item in a list + """ + key_or_value = 'value' + if key_type: + key_or_value = 'key' + valid_classes_phrase = get_valid_classes_phrase(valid_classes) + msg = ( + "Invalid type for variable '{0}'. Required {1} type {2} and " + "passed type was {3}".format( + var_name, + key_or_value, + valid_classes_phrase, + type(var_value).__name__, + ) + ) + return msg + + +def get_valid_classes_phrase(input_classes): + """Returns a string phrase describing what types are allowed + Note: Adds the extra valid classes in python2 + """ + all_classes = list(input_classes) + if six.PY2 and str in input_classes: + all_classes.extend([str_py2, unicode_py2]) + if six.PY2 and int in input_classes: + all_classes.extend([int_py2, long_py2]) + all_classes = sorted(all_classes, key=lambda cls: cls.__name__) + all_class_names = [cls.__name__ for cls in all_classes] + if len(all_class_names) == 1: + return 'is {0}'.format(all_class_names[0]) + return "is one of [{0}]".format(", ".join(all_class_names)) + + +def get_py3_class_name(input_class): + if six.PY2: + if input_class == str: + return 'str' + elif input_class == int: + return 'int' + return input_class.__name__ + + +def convert_js_args_to_python_args(fn): + from functools import wraps + @wraps(fn) + def wrapped_init(self, *args, **kwargs): + spec_property_naming = kwargs.get('_spec_property_naming', False) + if spec_property_naming: + kwargs = change_keys_js_to_python(kwargs, self.__class__) + return fn(self, *args, **kwargs) + return wrapped_init + + +def get_allof_instances(self, model_args, constant_args): + """ + Args: + self: the class we are handling + model_args (dict): var_name to var_value + used to make instances + constant_args (dict): var_name to var_value + used to make instances + + Returns + composed_instances (list) + """ + composed_instances = [] + for allof_class in self._composed_schemas['allOf']: + + # no need to handle changing js keys to python because + # for composed schemas, allof parameters are included in the + # composed schema and were changed to python keys in __new__ + # extract a dict of only required keys from fixed_model_args + kwargs = {} + var_names = set(allof_class.openapi_types.keys()) + for var_name in var_names: + if var_name in model_args: + kwargs[var_name] = model_args[var_name] + + # and use it to make the instance + kwargs.update(constant_args) + try: + allof_instance = allof_class(**kwargs) + composed_instances.append(allof_instance) + except Exception as ex: + six.raise_from(ApiValueError( + "Invalid inputs given to generate an instance of '%s'. The " + "input data was invalid for the allOf schema '%s' in the composed " + "schema '%s'. Error=%s" % ( + allof_class.__name__, + allof_class.__name__, + self.__class__.__name__, + str(ex) + ) + ), ex) + return composed_instances + + +def get_oneof_instance(self, model_args, constant_args): + """ + Find the oneOf schema that matches the input data (e.g. payload). + If exactly one schema matches the input data, an instance of that schema + is returned. + If zero or more than one schema match the input data, an exception is raised. + In OAS 3.x, the payload MUST, by validation, match exactly one of the + schemas described by oneOf. + Args: + self: the class we are handling + model_args (dict): var_name to var_value + The input data, e.g. the payload that must match a oneOf schema + in the OpenAPI document. + constant_args (dict): var_name to var_value + args that every model requires, including configuration, server + and path to item. + + Returns + oneof_instance (instance) + """ + if len(self._composed_schemas['oneOf']) == 0: + return None + + oneof_instances = [] + # Iterate over each oneOf schema and determine if the input data + # matches the oneOf schemas. + for oneof_class in self._composed_schemas['oneOf']: + # The composed oneOf schema allows the 'null' type and the input data + # is the null value. This is a OAS >= 3.1 feature. + if oneof_class is none_type: + # skip none_types because we are deserializing dict data. + # none_type deserialization is handled in the __new__ method + continue + + # transform js keys from input data to python keys in fixed_model_args + fixed_model_args = change_keys_js_to_python( + model_args, oneof_class) + + # Extract a dict with the properties that are declared in the oneOf schema. + # Undeclared properties (e.g. properties that are allowed because of the + # additionalProperties attribute in the OAS document) are not added to + # the dict. + kwargs = {} + var_names = set(oneof_class.openapi_types.keys()) + for var_name in var_names: + if var_name in fixed_model_args: + kwargs[var_name] = fixed_model_args[var_name] + + # do not try to make a model with no input args + if len(kwargs) == 0: + continue + + # and use it to make the instance + kwargs.update(constant_args) + try: + oneof_instance = oneof_class(**kwargs) + oneof_instances.append(oneof_instance) + except Exception: + pass + if len(oneof_instances) == 0: + raise ApiValueError( + "Invalid inputs given to generate an instance of %s. None " + "of the oneOf schemas matched the input data." % + self.__class__.__name__ + ) + elif len(oneof_instances) > 1: + raise ApiValueError( + "Invalid inputs given to generate an instance of %s. Multiple " + "oneOf schemas matched the inputs, but a max of one is allowed." % + self.__class__.__name__ + ) + return oneof_instances[0] + + +def get_anyof_instances(self, model_args, constant_args): + """ + Args: + self: the class we are handling + model_args (dict): var_name to var_value + The input data, e.g. the payload that must match at least one + anyOf child schema in the OpenAPI document. + constant_args (dict): var_name to var_value + args that every model requires, including configuration, server + and path to item. + + Returns + anyof_instances (list) + """ + anyof_instances = [] + if len(self._composed_schemas['anyOf']) == 0: + return anyof_instances + + for anyof_class in self._composed_schemas['anyOf']: + # The composed oneOf schema allows the 'null' type and the input data + # is the null value. This is a OAS >= 3.1 feature. + if anyof_class is none_type: + # skip none_types because we are deserializing dict data. + # none_type deserialization is handled in the __new__ method + continue + + # transform js keys to python keys in fixed_model_args + fixed_model_args = change_keys_js_to_python(model_args, anyof_class) + + # extract a dict of only required keys from these_model_vars + kwargs = {} + var_names = set(anyof_class.openapi_types.keys()) + for var_name in var_names: + if var_name in fixed_model_args: + kwargs[var_name] = fixed_model_args[var_name] + + # do not try to make a model with no input args + if len(kwargs) == 0: + continue + + # and use it to make the instance + kwargs.update(constant_args) + try: + anyof_instance = anyof_class(**kwargs) + anyof_instances.append(anyof_instance) + except Exception: + pass + if len(anyof_instances) == 0: + raise ApiValueError( + "Invalid inputs given to generate an instance of %s. None of the " + "anyOf schemas matched the inputs." % + self.__class__.__name__ + ) + return anyof_instances + + +def get_additional_properties_model_instances( + composed_instances, self): + additional_properties_model_instances = [] + all_instances = [self] + all_instances.extend(composed_instances) + for instance in all_instances: + if instance.additional_properties_type is not None: + additional_properties_model_instances.append(instance) + return additional_properties_model_instances + + +def get_var_name_to_model_instances(self, composed_instances): + var_name_to_model_instances = {} + all_instances = [self] + all_instances.extend(composed_instances) + for instance in all_instances: + for var_name in instance.openapi_types: + if var_name not in var_name_to_model_instances: + var_name_to_model_instances[var_name] = [instance] + else: + var_name_to_model_instances[var_name].append(instance) + return var_name_to_model_instances + + +def get_unused_args(self, composed_instances, model_args): + unused_args = dict(model_args) + # arguments apssed to self were already converted to python names + # before __init__ was called + for var_name_py in self.attribute_map: + if var_name_py in unused_args: + del unused_args[var_name_py] + for instance in composed_instances: + if instance.__class__ in self._composed_schemas['allOf']: + for var_name_py in instance.attribute_map: + if var_name_py in unused_args: + del unused_args[var_name_py] + else: + for var_name_js in instance.attribute_map.values(): + if var_name_js in unused_args: + del unused_args[var_name_js] + return unused_args + + +def validate_get_composed_info(constant_args, model_args, self): + """ + For composed schemas, generate schema instances for + all schemas in the oneOf/anyOf/allOf definition. If additional + properties are allowed, also assign those properties on + all matched schemas that contain additionalProperties. + Openapi schemas are python classes. + + Exceptions are raised if: + - 0 or > 1 oneOf schema matches the model_args input data + - no anyOf schema matches the model_args input data + - any of the allOf schemas do not match the model_args input data + + Args: + constant_args (dict): these are the args that every model requires + model_args (dict): these are the required and optional spec args that + were passed in to make this model + self (class): the class that we are instantiating + This class contains self._composed_schemas + + Returns: + composed_info (list): length three + composed_instances (list): the composed instances which are not + self + var_name_to_model_instances (dict): a dict going from var_name + to the model_instance which holds that var_name + the model_instance may be self or an instance of one of the + classes in self.composed_instances() + additional_properties_model_instances (list): a list of the + model instances which have the property + additional_properties_type. This list can include self + """ + # create composed_instances + composed_instances = [] + allof_instances = get_allof_instances(self, model_args, constant_args) + composed_instances.extend(allof_instances) + oneof_instance = get_oneof_instance(self, model_args, constant_args) + if oneof_instance is not None: + composed_instances.append(oneof_instance) + anyof_instances = get_anyof_instances(self, model_args, constant_args) + composed_instances.extend(anyof_instances) + + # map variable names to composed_instances + var_name_to_model_instances = get_var_name_to_model_instances( + self, composed_instances) + + # set additional_properties_model_instances + additional_properties_model_instances = ( + get_additional_properties_model_instances(composed_instances, self) + ) + + # set any remaining values + unused_args = get_unused_args(self, composed_instances, model_args) + if len(unused_args) > 0 and \ + len(additional_properties_model_instances) == 0 and \ + (self._configuration is None or + not self._configuration.discard_unknown_keys): + raise ApiValueError( + "Invalid input arguments input when making an instance of " + "class %s. Not all inputs were used. The unused input data " + "is %s" % (self.__class__.__name__, unused_args) + ) + + # no need to add additional_properties to var_name_to_model_instances here + # because additional_properties_model_instances will direct us to that + # instance when we use getattr or setattr + # and we update var_name_to_model_instances in setattr + + return [ + composed_instances, + var_name_to_model_instances, + additional_properties_model_instances, + unused_args + ] diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/models/__init__.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/models/__init__.py new file mode 100644 index 000000000000..57bbacb357b7 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/models/__init__.py @@ -0,0 +1,13 @@ +# coding: utf-8 + +# flake8: noqa + +# import all models into this package +# if you have many models here with many references from one model to another this may +# raise a RecursionError +# to avoid this, import only the models that you directly need like: +# from from x_auth_id_alias.model.pet import Pet +# or import this package, but before doing it, use: +# import sys +# sys.setrecursionlimit(n) + diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/rest.py b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/rest.py new file mode 100644 index 000000000000..354091c3a977 --- /dev/null +++ b/samples/openapi3/client/extensions/x-auth-id-alias/python-experimental/x_auth_id_alias/rest.py @@ -0,0 +1,291 @@ +# coding: utf-8 + +""" + OpenAPI Extension x-auth-id-alias + + This specification shows how to use x-auth-id-alias extension for API keys. # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +from __future__ import absolute_import + +import io +import json +import logging +import re +import ssl + +import certifi +# python 2 and python 3 compatibility library +import six +from six.moves.urllib.parse import urlencode +import urllib3 + +from x_auth_id_alias.exceptions import ApiException, ApiValueError + + +logger = logging.getLogger(__name__) + + +class RESTResponse(io.IOBase): + + def __init__(self, resp): + self.urllib3_response = resp + self.status = resp.status + self.reason = resp.reason + self.data = resp.data + + def getheaders(self): + """Returns a dictionary of the response headers.""" + return self.urllib3_response.getheaders() + + def getheader(self, name, default=None): + """Returns a given response header.""" + return self.urllib3_response.getheader(name, default) + + +class RESTClientObject(object): + + def __init__(self, configuration, pools_size=4, maxsize=None): + # urllib3.PoolManager will pass all kw parameters to connectionpool + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # noqa: E501 + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # noqa: E501 + # maxsize is the number of requests to host that are allowed in parallel # noqa: E501 + # Custom SSL certificates and client certificates: http://urllib3.readthedocs.io/en/latest/advanced-usage.html # noqa: E501 + + # cert_reqs + if configuration.verify_ssl: + cert_reqs = ssl.CERT_REQUIRED + else: + cert_reqs = ssl.CERT_NONE + + # ca_certs + if configuration.ssl_ca_cert: + ca_certs = configuration.ssl_ca_cert + else: + # if not set certificate file, use Mozilla's root certificates. + ca_certs = certifi.where() + + addition_pool_args = {} + if configuration.assert_hostname is not None: + addition_pool_args['assert_hostname'] = configuration.assert_hostname # noqa: E501 + + if configuration.retries is not None: + addition_pool_args['retries'] = configuration.retries + + if maxsize is None: + if configuration.connection_pool_maxsize is not None: + maxsize = configuration.connection_pool_maxsize + else: + maxsize = 4 + + # https pool manager + if configuration.proxy: + self.pool_manager = urllib3.ProxyManager( + num_pools=pools_size, + maxsize=maxsize, + cert_reqs=cert_reqs, + ca_certs=ca_certs, + cert_file=configuration.cert_file, + key_file=configuration.key_file, + proxy_url=configuration.proxy, + proxy_headers=configuration.proxy_headers, + **addition_pool_args + ) + else: + self.pool_manager = urllib3.PoolManager( + num_pools=pools_size, + maxsize=maxsize, + cert_reqs=cert_reqs, + ca_certs=ca_certs, + cert_file=configuration.cert_file, + key_file=configuration.key_file, + **addition_pool_args + ) + + def request(self, method, url, query_params=None, headers=None, + body=None, post_params=None, _preload_content=True, + _request_timeout=None): + """Perform requests. + + :param method: http request method + :param url: http request url + :param query_params: query parameters in the url + :param headers: http request headers + :param body: request json body, for `application/json` + :param post_params: request post parameters, + `application/x-www-form-urlencoded` + and `multipart/form-data` + :param _preload_content: if False, the urllib3.HTTPResponse object will + be returned without reading/decoding response + data. Default is True. + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + """ + method = method.upper() + assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT', + 'PATCH', 'OPTIONS'] + + if post_params and body: + raise ApiValueError( + "body parameter cannot be used with post_params parameter." + ) + + post_params = post_params or {} + headers = headers or {} + + timeout = None + if _request_timeout: + if isinstance(_request_timeout, (int, ) if six.PY3 else (int, long)): # noqa: E501,F821 + timeout = urllib3.Timeout(total=_request_timeout) + elif (isinstance(_request_timeout, tuple) and + len(_request_timeout) == 2): + timeout = urllib3.Timeout( + connect=_request_timeout[0], read=_request_timeout[1]) + + if 'Content-Type' not in headers: + headers['Content-Type'] = 'application/json' + + try: + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` + if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']: + if query_params: + url += '?' + urlencode(query_params) + if re.search('json', headers['Content-Type'], re.IGNORECASE): + request_body = None + if body is not None: + request_body = json.dumps(body) + r = self.pool_manager.request( + method, url, + body=request_body, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 + r = self.pool_manager.request( + method, url, + fields=post_params, + encode_multipart=False, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + elif headers['Content-Type'] == 'multipart/form-data': + # must del headers['Content-Type'], or the correct + # Content-Type which generated by urllib3 will be + # overwritten. + del headers['Content-Type'] + r = self.pool_manager.request( + method, url, + fields=post_params, + encode_multipart=True, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + # Pass a `string` parameter directly in the body to support + # other content types than Json when `body` argument is + # provided in serialized form + elif isinstance(body, str) or isinstance(body, bytes): + request_body = body + r = self.pool_manager.request( + method, url, + body=request_body, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + else: + # Cannot generate the request from given parameters + msg = """Cannot prepare a request message for provided + arguments. Please check that your arguments match + declared content type.""" + raise ApiException(status=0, reason=msg) + # For `GET`, `HEAD` + else: + r = self.pool_manager.request(method, url, + fields=query_params, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + except urllib3.exceptions.SSLError as e: + msg = "{0}\n{1}".format(type(e).__name__, str(e)) + raise ApiException(status=0, reason=msg) + + if _preload_content: + r = RESTResponse(r) + + # log response body + logger.debug("response body: %s", r.data) + + if not 200 <= r.status <= 299: + raise ApiException(http_resp=r) + + return r + + def GET(self, url, headers=None, query_params=None, _preload_content=True, + _request_timeout=None): + return self.request("GET", url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params) + + def HEAD(self, url, headers=None, query_params=None, _preload_content=True, + _request_timeout=None): + return self.request("HEAD", url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params) + + def OPTIONS(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("OPTIONS", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def DELETE(self, url, headers=None, query_params=None, body=None, + _preload_content=True, _request_timeout=None): + return self.request("DELETE", url, + headers=headers, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def POST(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("POST", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def PUT(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("PUT", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def PATCH(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("PATCH", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/configuration.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/configuration.py index 850cb907b09f..46432be2cea8 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/configuration.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/configuration.py @@ -396,15 +396,16 @@ def logger_format(self, value): self.__logger_format = value self.logger_formatter = logging.Formatter(self.__logger_format) - def get_api_key_with_prefix(self, identifier): + def get_api_key_with_prefix(self, identifier, alias=None): """Gets API key (with prefix if set). :param identifier: The identifier of apiKey. + :param alias: The alternative identifier of apiKey. :return: The token for api key authentication. """ if self.refresh_api_key_hook is not None: self.refresh_api_key_hook(self) - key = self.api_key.get(identifier) + key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) if key: prefix = self.api_key_prefix.get(identifier) if prefix: @@ -438,14 +439,18 @@ def auth_settings(self): 'type': 'api_key', 'in': 'header', 'key': 'api_key', - 'value': self.get_api_key_with_prefix('api_key') + 'value': self.get_api_key_with_prefix( + 'api_key', + ), } if 'api_key_query' in self.api_key: auth['api_key_query'] = { 'type': 'api_key', 'in': 'query', 'key': 'api_key_query', - 'value': self.get_api_key_with_prefix('api_key_query') + 'value': self.get_api_key_with_prefix( + 'api_key_query', + ), } if self.access_token is not None: auth['bearer_test'] = { diff --git a/samples/openapi3/client/petstore/python/petstore_api/configuration.py b/samples/openapi3/client/petstore/python/petstore_api/configuration.py index 850cb907b09f..46432be2cea8 100644 --- a/samples/openapi3/client/petstore/python/petstore_api/configuration.py +++ b/samples/openapi3/client/petstore/python/petstore_api/configuration.py @@ -396,15 +396,16 @@ def logger_format(self, value): self.__logger_format = value self.logger_formatter = logging.Formatter(self.__logger_format) - def get_api_key_with_prefix(self, identifier): + def get_api_key_with_prefix(self, identifier, alias=None): """Gets API key (with prefix if set). :param identifier: The identifier of apiKey. + :param alias: The alternative identifier of apiKey. :return: The token for api key authentication. """ if self.refresh_api_key_hook is not None: self.refresh_api_key_hook(self) - key = self.api_key.get(identifier) + key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) if key: prefix = self.api_key_prefix.get(identifier) if prefix: @@ -438,14 +439,18 @@ def auth_settings(self): 'type': 'api_key', 'in': 'header', 'key': 'api_key', - 'value': self.get_api_key_with_prefix('api_key') + 'value': self.get_api_key_with_prefix( + 'api_key', + ), } if 'api_key_query' in self.api_key: auth['api_key_query'] = { 'type': 'api_key', 'in': 'query', 'key': 'api_key_query', - 'value': self.get_api_key_with_prefix('api_key_query') + 'value': self.get_api_key_with_prefix( + 'api_key_query', + ), } if self.access_token is not None: auth['bearer_test'] = {