├── .github └── workflows │ ├── deutschland_generator.yaml │ └── openapi_check.yaml ├── CNAME ├── README.md ├── generator_config.yaml ├── index.html ├── openapi.yaml └── python-client ├── .openapi-generator-ignore ├── .openapi-generator ├── FILES └── VERSION ├── README.md ├── deutschland └── risikogebiete │ ├── __init__.py │ ├── api │ ├── __init__.py │ └── default_api.py │ ├── api_client.py │ ├── apis │ └── __init__.py │ ├── configuration.py │ ├── exceptions.py │ ├── model │ ├── __init__.py │ └── risk_countries.py │ ├── model_utils.py │ ├── models │ └── __init__.py │ └── rest.py ├── docs ├── DefaultApi.md └── RiskCountries.md ├── pyproject.toml ├── requirements.txt ├── sphinx-docs ├── Makefile ├── conf.py ├── index.rst ├── make.bat └── source │ ├── modules.rst │ ├── risikogebiete.api.rst │ ├── risikogebiete.apis.rst │ ├── risikogebiete.model.rst │ ├── risikogebiete.models.rst │ └── risikogebiete.rst ├── test-requirements.txt ├── test ├── __init__.py ├── test_default_api.py └── test_risk_countries.py └── tox.ini /.github/workflows/deutschland_generator.yaml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | jobs: 3 | deutschland_generation: 4 | name: "Deutschland Generation" 5 | runs-on: ubuntu-latest 6 | strategy: 7 | fail-fast: false 8 | matrix: 9 | python-version: ['3.7.8' ] 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | # Create default .spectral.yaml file used for linting if its not existing already 14 | - name: "Create spectral file if it not exists" 15 | continue-on-error: true 16 | run: | 17 | set -C; echo "extends: spectral:oas" > .spectral.yaml 18 | 19 | # Runs a single command using the runners shell 20 | - name: "Lint file" 21 | uses: stoplightio/spectral-action@v0.8.1 22 | with: 23 | file_glob: "openapi.yaml" 24 | 25 | - name: "Generate deutschland code" 26 | uses: wirthual/deutschland-generator-action@latest 27 | with: 28 | openapi-file: ${{ github.workspace }}/openapi.yaml 29 | commit-to-git: true 30 | upload-to-pypi: true 31 | upload-to-testpypi: false 32 | pypi-token: ${{ secrets.PYPI_PRODUCTION }} 33 | testpypi-token: ${{ secrets.PYPI_TEST }} 34 | python-version: ${{ matrix.python-version }} -------------------------------------------------------------------------------- /.github/workflows/openapi_check.yaml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | jobs: 3 | openapi_check: 4 | name: "OpenAPI check" 5 | runs-on: ubuntu-latest 6 | steps: 7 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 8 | - uses: actions/checkout@v2 9 | 10 | # Create default .spectral.yaml file used for linting if its not existing already 11 | - name: "Create spectral file if it not exists" 12 | continue-on-error: true 13 | run: | 14 | set -C; echo "extends: spectral:oas" > .spectral.yaml 15 | 16 | # Run Spectral 17 | - uses: stoplightio/spectral-action@v0.8.1 18 | with: 19 | file_glob: openapi.yaml 20 | spectral_ruleset: .spectral.yaml 21 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | risikogebiete.api.bund.dev -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # risikogebiete-api 2 | Corona Risikogebiete vom RKI. [Dokumentation](https://risikogebiete.api.bund.dev/) 3 | -------------------------------------------------------------------------------- /generator_config.yaml: -------------------------------------------------------------------------------- 1 | templateDir: deutschland_templates # For local use: ./local/deutschland_templates 2 | additionalProperties: 3 | packageName: "risikogebiete" 4 | infoName: "BundesAPI" 5 | infoEmail: "kontakt@bund.dev" 6 | packageVersion: 0.1.0 7 | packageUrl: "https://github.com/bundesAPI/risikogebiete-api" 8 | namespace: "deutschland" 9 | docLanguage: "de" 10 | gitHost: "github.com" 11 | gitUserId: "bundesAPI" 12 | gitRepoId: "risikogebiete" 13 | files: 14 | pyproject.mustache: 15 | destinationFilename: pyproject.toml 16 | templateType: SupportingFiles 17 | requirements.txt: {} 18 | create_doc.mustache: 19 | destinationFilename: create_doc.py 20 | templateType: SupportingFiles 21 | rename_generated_code.mustache: 22 | destinationFilename: rename_generated_code.py 23 | templateType: SupportingFiles 24 | README.mustache: 25 | destinationFilename: README.md 26 | templateType: SupportingFiles 27 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Robert Koch Institut - Corona Risikogebiete API - OpenAPI Documentation 8 | 9 | 10 |
11 | 12 | 13 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /openapi.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | description: "Aktuelle Corona Risikogebietsinformationen als API" 4 | version: "1.0.0" 5 | title: "Robert Koch Institut: Corona Risikogebiete API" 6 | 7 | servers: 8 | - url: 'https://api.einreiseanmeldung.de/reisendenportal' 9 | 10 | paths: 11 | /risikogebiete: 12 | get: 13 | description: Liste aller Länder mit Risikoeinstufung 14 | summary: Liste der Länder 15 | responses: 16 | '200': 17 | description: OK 18 | content: 19 | application/json: 20 | schema: 21 | $ref: '#/components/schemas/RiskCountries' 22 | 23 | components: 24 | schemas: 25 | RiskCountries: 26 | type: array 27 | items: 28 | type: object 29 | properties: 30 | longName: 31 | type: string 32 | example: Königreich Kambodscha 33 | riskClass: 34 | type: string 35 | nullable: true 36 | regions: 37 | type: array 38 | items: 39 | type: object 40 | properties: 41 | name: 42 | type: string 43 | example: Französisch-Guayana / French Guiana 44 | type: 45 | type: string 46 | example: Überseegebiete / overseas territories 47 | riskClass: 48 | type: string 49 | example: Hochinzidenzgebiet / high incidence area 50 | createdAt: 51 | type: string 52 | example: 2021-07-26T22:00:08.412961 53 | updatedAt: 54 | type: string 55 | example: 2021-07-26T22:00:08.412961 56 | id: 57 | type: string 58 | example: KH 59 | name: 60 | type: string 61 | example: Kambodscha / Cambodia 62 | -------------------------------------------------------------------------------- /python-client/.openapi-generator-ignore: -------------------------------------------------------------------------------- 1 | # OpenAPI Generator Ignore 2 | # Generated by openapi-generator https://github.com/openapitools/openapi-generator 3 | 4 | # Use this file to prevent files from being overwritten by the generator. 5 | # The patterns follow closely to .gitignore or .dockerignore. 6 | 7 | # As an example, the C# client generator defines ApiClient.cs. 8 | # You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: 9 | #ApiClient.cs 10 | 11 | # You can match any string of characters against a directory, file or extension with a single asterisk (*): 12 | #foo/*/qux 13 | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux 14 | 15 | # You can recursively match patterns against a directory, file or extension with a double asterisk (**): 16 | #foo/**/qux 17 | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux 18 | 19 | # You can also negate patterns with an exclamation (!). 20 | # For example, you can ignore all files in a docs folder with the file extension .md: 21 | #docs/*.md 22 | # Then explicitly reverse the ignore rule for a single file: 23 | #!docs/README.md 24 | -------------------------------------------------------------------------------- /python-client/.openapi-generator/FILES: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .gitlab-ci.yml 3 | .openapi-generator-ignore 4 | .travis.yml 5 | README.md 6 | create_doc.py 7 | docs/DefaultApi.md 8 | docs/RiskCountries.md 9 | git_push.sh 10 | pyproject.toml 11 | rename_generated_code.py 12 | requirements.txt 13 | requirements.txt 14 | risikogebiete/__init__.py 15 | risikogebiete/api/__init__.py 16 | risikogebiete/api/default_api.py 17 | risikogebiete/api_client.py 18 | risikogebiete/apis/__init__.py 19 | risikogebiete/configuration.py 20 | risikogebiete/exceptions.py 21 | risikogebiete/model/__init__.py 22 | risikogebiete/model/risk_countries.py 23 | risikogebiete/model_utils.py 24 | risikogebiete/models/__init__.py 25 | risikogebiete/rest.py 26 | setup.cfg 27 | setup.py 28 | test-requirements.txt 29 | test/__init__.py 30 | test/test_default_api.py 31 | test/test_risk_countries.py 32 | tox.ini 33 | -------------------------------------------------------------------------------- /python-client/.openapi-generator/VERSION: -------------------------------------------------------------------------------- 1 | 6.0.0-SNAPSHOT -------------------------------------------------------------------------------- /python-client/README.md: -------------------------------------------------------------------------------- 1 | # risikogebiete 2 | Aktuelle Corona Risikogebietsinformationen als API 3 | 4 | This Python package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: 5 | 6 | - API version: 1.0.0 7 | - Package version: 0.1.0 8 | - Build package: org.openapitools.codegen.languages.PythonClientCodegen 9 | 10 | ## Requirements. 11 | 12 | Python >= 3.6 13 | 14 | ## Installation & Usage 15 | ### pip install 16 | 17 | ```sh 18 | pip install deutschland[risikogebiete] 19 | ``` 20 | 21 | ### poetry install 22 | 23 | ```sh 24 | poetry add deutschland -E risikogebiete 25 | ``` 26 | 27 | ### Setuptools 28 | 29 | Install via [Setuptools](http://pypi.python.org/pypi/setuptools). 30 | 31 | ```sh 32 | python setup.py install --user 33 | ``` 34 | (or `sudo python setup.py install` to install the package for all users) 35 | 36 | ## Usage 37 | 38 | Import the package: 39 | ```python 40 | from deutschland import risikogebiete 41 | ``` 42 | 43 | ## Getting Started 44 | 45 | Please follow the [installation procedure](#installation--usage) and then run the following: 46 | 47 | ```python 48 | 49 | import time 50 | from deutschland import risikogebiete 51 | from pprint import pprint 52 | from deutschland.risikogebiete.api import default_api 53 | from deutschland.risikogebiete.model.risk_countries import RiskCountries 54 | # Defining the host is optional and defaults to https://api.einreiseanmeldung.de/reisendenportal 55 | # See configuration.py for a list of all supported configuration parameters. 56 | configuration = risikogebiete.Configuration( 57 | host = "https://api.einreiseanmeldung.de/reisendenportal" 58 | ) 59 | 60 | 61 | 62 | # Enter a context with an instance of the API client 63 | with risikogebiete.ApiClient(configuration) as api_client: 64 | # Create an instance of the API class 65 | api_instance = default_api.DefaultApi(api_client) 66 | 67 | try: 68 | # Liste der Länder 69 | api_response = api_instance.risikogebiete_get() 70 | pprint(api_response) 71 | except risikogebiete.ApiException as e: 72 | print("Exception when calling DefaultApi->risikogebiete_get: %s\n" % e) 73 | ``` 74 | 75 | ## Documentation for API Endpoints 76 | 77 | All URIs are relative to *https://api.einreiseanmeldung.de/reisendenportal* 78 | 79 | Class | Method | HTTP request | Description 80 | ------------ | ------------- | ------------- | ------------- 81 | *DefaultApi* | [**risikogebiete_get**](docs/DefaultApi.md#risikogebiete_get) | **GET** /risikogebiete | Liste der Länder 82 | 83 | 84 | ## Documentation For Models 85 | 86 | - [RiskCountries](docs/RiskCountries.md) 87 | 88 | 89 | ## Documentation For Authorization 90 | 91 | All endpoints do not require authorization. 92 | 93 | ## Author 94 | 95 | kontakt@bund.dev 96 | 97 | 98 | ## Notes for Large OpenAPI documents 99 | If the OpenAPI document is large, imports in risikogebiete.apis and risikogebiete.models may fail with a 100 | RecursionError indicating the maximum recursion limit has been exceeded. In that case, there are a couple of solutions: 101 | 102 | Solution 1: 103 | Use specific imports for apis and models like: 104 | - `from deutschland.risikogebiete.api.default_api import DefaultApi` 105 | - `from deutschland.risikogebiete.model.pet import Pet` 106 | 107 | Solution 2: 108 | Before importing the package, adjust the maximum recursion limit as shown below: 109 | ``` 110 | import sys 111 | sys.setrecursionlimit(1500) 112 | from deutschland import risikogebiete 113 | from deutschland.risikogebiete.apis import * 114 | from deutschland.risikogebiete.models import * 115 | ``` 116 | 117 | -------------------------------------------------------------------------------- /python-client/deutschland/risikogebiete/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | """ 4 | Robert Koch Institut: Corona Risikogebiete API 5 | 6 | Aktuelle Corona Risikogebietsinformationen als API # noqa: E501 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Contact: kontakt@bund.dev 10 | Generated by: https://openapi-generator.tech 11 | """ 12 | 13 | 14 | __version__ = "0.1.0" 15 | 16 | # import ApiClient 17 | from deutschland.risikogebiete.api_client import ApiClient 18 | 19 | # import Configuration 20 | from deutschland.risikogebiete.configuration import Configuration 21 | 22 | # import exceptions 23 | from deutschland.risikogebiete.exceptions import ( 24 | ApiAttributeError, 25 | ApiException, 26 | ApiKeyError, 27 | ApiTypeError, 28 | ApiValueError, 29 | OpenApiException, 30 | ) 31 | -------------------------------------------------------------------------------- /python-client/deutschland/risikogebiete/api/__init__.py: -------------------------------------------------------------------------------- 1 | # do not import all apis into this module because that uses a lot of memory and stack frames 2 | # if you need the ability to import all apis from one package, import them with 3 | # from deutschland.risikogebiete.apis import DefaultApi 4 | -------------------------------------------------------------------------------- /python-client/deutschland/risikogebiete/api/default_api.py: -------------------------------------------------------------------------------- 1 | """ 2 | Robert Koch Institut: Corona Risikogebiete API 3 | 4 | Aktuelle Corona Risikogebietsinformationen als API # noqa: E501 5 | 6 | The version of the OpenAPI document: 1.0.0 7 | Contact: kontakt@bund.dev 8 | Generated by: https://openapi-generator.tech 9 | """ 10 | 11 | 12 | import re # noqa: F401 13 | import sys # noqa: F401 14 | 15 | from deutschland.risikogebiete.api_client import ApiClient 16 | from deutschland.risikogebiete.api_client import Endpoint as _Endpoint 17 | from deutschland.risikogebiete.model.risk_countries import RiskCountries 18 | from deutschland.risikogebiete.model_utils import ( # noqa: F401 19 | check_allowed_values, 20 | check_validations, 21 | date, 22 | datetime, 23 | file_type, 24 | none_type, 25 | validate_and_convert_types, 26 | ) 27 | 28 | 29 | class DefaultApi(object): 30 | """NOTE: This class is auto generated by OpenAPI Generator 31 | Ref: https://openapi-generator.tech 32 | 33 | Do not edit the class manually. 34 | """ 35 | 36 | def __init__(self, api_client=None): 37 | if api_client is None: 38 | api_client = ApiClient() 39 | self.api_client = api_client 40 | self.risikogebiete_get_endpoint = _Endpoint( 41 | settings={ 42 | "response_type": (RiskCountries,), 43 | "auth": [], 44 | "endpoint_path": "/risikogebiete", 45 | "operation_id": "risikogebiete_get", 46 | "http_method": "GET", 47 | "servers": None, 48 | }, 49 | params_map={ 50 | "all": [], 51 | "required": [], 52 | "nullable": [], 53 | "enum": [], 54 | "validation": [], 55 | }, 56 | root_map={ 57 | "validations": {}, 58 | "allowed_values": {}, 59 | "openapi_types": {}, 60 | "attribute_map": {}, 61 | "location_map": {}, 62 | "collection_format_map": {}, 63 | }, 64 | headers_map={ 65 | "accept": ["application/json"], 66 | "content_type": [], 67 | }, 68 | api_client=api_client, 69 | ) 70 | 71 | def risikogebiete_get(self, **kwargs): 72 | """Liste der Länder # noqa: E501 73 | 74 | Liste aller Länder mit Risikoeinstufung # noqa: E501 75 | This method makes a synchronous HTTP request by default. To make an 76 | asynchronous HTTP request, please pass async_req=True 77 | 78 | >>> thread = api.risikogebiete_get(async_req=True) 79 | >>> result = thread.get() 80 | 81 | 82 | Keyword Args: 83 | _return_http_data_only (bool): response data without head status 84 | code and headers. Default is True. 85 | _preload_content (bool): if False, the urllib3.HTTPResponse object 86 | will be returned without reading/decoding response data. 87 | Default is True. 88 | _request_timeout (int/float/tuple): timeout setting for this request. If 89 | one number provided, it will be total request timeout. It can also 90 | be a pair (tuple) of (connection, read) timeouts. 91 | Default is None. 92 | _check_input_type (bool): specifies if type checking 93 | should be done one the data sent to the server. 94 | Default is True. 95 | _check_return_type (bool): specifies if type checking 96 | should be done one the data received from the server. 97 | Default is True. 98 | _spec_property_naming (bool): True if the variable names in the input data 99 | are serialized names, as specified in the OpenAPI document. 100 | False if the variable names in the input data 101 | are pythonic names, e.g. snake case (default) 102 | _content_type (str/None): force body content-type. 103 | Default is None and content-type will be predicted by allowed 104 | content-types and body. 105 | _host_index (int/None): specifies the index of the server 106 | that we want to use. 107 | Default is read from the configuration. 108 | async_req (bool): execute request asynchronously 109 | 110 | Returns: 111 | RiskCountries 112 | If the method is called asynchronously, returns the request 113 | thread. 114 | """ 115 | kwargs["async_req"] = kwargs.get("async_req", False) 116 | kwargs["_return_http_data_only"] = kwargs.get("_return_http_data_only", True) 117 | kwargs["_preload_content"] = kwargs.get("_preload_content", True) 118 | kwargs["_request_timeout"] = kwargs.get("_request_timeout", None) 119 | kwargs["_check_input_type"] = kwargs.get("_check_input_type", True) 120 | kwargs["_check_return_type"] = kwargs.get("_check_return_type", True) 121 | kwargs["_spec_property_naming"] = kwargs.get("_spec_property_naming", False) 122 | kwargs["_content_type"] = kwargs.get("_content_type") 123 | kwargs["_host_index"] = kwargs.get("_host_index") 124 | return self.risikogebiete_get_endpoint.call_with_http_info(**kwargs) 125 | -------------------------------------------------------------------------------- /python-client/deutschland/risikogebiete/api_client.py: -------------------------------------------------------------------------------- 1 | """ 2 | Robert Koch Institut: Corona Risikogebiete API 3 | 4 | Aktuelle Corona Risikogebietsinformationen als API # noqa: E501 5 | 6 | The version of the OpenAPI document: 1.0.0 7 | Contact: kontakt@bund.dev 8 | Generated by: https://openapi-generator.tech 9 | """ 10 | 11 | 12 | import atexit 13 | import io 14 | import json 15 | import mimetypes 16 | import os 17 | import re 18 | import typing 19 | from multiprocessing.pool import ThreadPool 20 | from urllib.parse import quote 21 | 22 | from deutschland.risikogebiete import rest 23 | from deutschland.risikogebiete.configuration import Configuration 24 | from deutschland.risikogebiete.exceptions import ( 25 | ApiException, 26 | ApiTypeError, 27 | ApiValueError, 28 | ) 29 | from deutschland.risikogebiete.model_utils import ( 30 | ModelComposed, 31 | ModelNormal, 32 | ModelSimple, 33 | check_allowed_values, 34 | check_validations, 35 | date, 36 | datetime, 37 | deserialize_file, 38 | file_type, 39 | model_to_dict, 40 | none_type, 41 | validate_and_convert_types, 42 | ) 43 | from urllib3.fields import RequestField 44 | 45 | 46 | class ApiClient(object): 47 | """Generic API client for OpenAPI client library builds. 48 | 49 | OpenAPI generic API client. This client handles the client- 50 | server communication, and is invariant across implementations. Specifics of 51 | the methods and models for each application are generated from the OpenAPI 52 | templates. 53 | 54 | NOTE: This class is auto generated by OpenAPI Generator. 55 | Ref: https://openapi-generator.tech 56 | Do not edit the class manually. 57 | 58 | :param configuration: .Configuration object for this client 59 | :param header_name: a header to pass when making calls to the API. 60 | :param header_value: a header value to pass when making calls to 61 | the API. 62 | :param cookie: a cookie to include in the header when making calls 63 | to the API 64 | :param pool_threads: The number of threads to use for async requests 65 | to the API. More threads means more concurrent API requests. 66 | """ 67 | 68 | _pool = None 69 | 70 | def __init__( 71 | self, 72 | configuration=None, 73 | header_name=None, 74 | header_value=None, 75 | cookie=None, 76 | pool_threads=1, 77 | ): 78 | if configuration is None: 79 | configuration = Configuration.get_default_copy() 80 | self.configuration = configuration 81 | self.pool_threads = pool_threads 82 | 83 | self.rest_client = rest.RESTClientObject(configuration) 84 | self.default_headers = {} 85 | if header_name is not None: 86 | self.default_headers[header_name] = header_value 87 | self.cookie = cookie 88 | # Set default User-Agent. 89 | self.user_agent = "OpenAPI-Generator/0.1.0/python" 90 | 91 | def __enter__(self): 92 | return self 93 | 94 | def __exit__(self, exc_type, exc_value, traceback): 95 | self.close() 96 | 97 | def close(self): 98 | if self._pool: 99 | self._pool.close() 100 | self._pool.join() 101 | self._pool = None 102 | if hasattr(atexit, "unregister"): 103 | atexit.unregister(self.close) 104 | 105 | @property 106 | def pool(self): 107 | """Create thread pool on first request 108 | avoids instantiating unused threadpool for blocking clients. 109 | """ 110 | if self._pool is None: 111 | atexit.register(self.close) 112 | self._pool = ThreadPool(self.pool_threads) 113 | return self._pool 114 | 115 | @property 116 | def user_agent(self): 117 | """User agent for this API client""" 118 | return self.default_headers["User-Agent"] 119 | 120 | @user_agent.setter 121 | def user_agent(self, value): 122 | self.default_headers["User-Agent"] = value 123 | 124 | def set_default_header(self, header_name, header_value): 125 | self.default_headers[header_name] = header_value 126 | 127 | def __call_api( 128 | self, 129 | resource_path: str, 130 | method: str, 131 | path_params: typing.Optional[typing.Dict[str, typing.Any]] = None, 132 | query_params: typing.Optional[ 133 | typing.List[typing.Tuple[str, typing.Any]] 134 | ] = None, 135 | header_params: typing.Optional[typing.Dict[str, typing.Any]] = None, 136 | body: typing.Optional[typing.Any] = None, 137 | post_params: typing.Optional[typing.List[typing.Tuple[str, typing.Any]]] = None, 138 | files: typing.Optional[typing.Dict[str, typing.List[io.IOBase]]] = None, 139 | response_type: typing.Optional[typing.Tuple[typing.Any]] = None, 140 | auth_settings: typing.Optional[typing.List[str]] = None, 141 | _return_http_data_only: typing.Optional[bool] = None, 142 | collection_formats: typing.Optional[typing.Dict[str, str]] = None, 143 | _preload_content: bool = True, 144 | _request_timeout: typing.Optional[ 145 | typing.Union[int, float, typing.Tuple] 146 | ] = None, 147 | _host: typing.Optional[str] = None, 148 | _check_type: typing.Optional[bool] = None, 149 | _content_type: typing.Optional[str] = None, 150 | ): 151 | 152 | config = self.configuration 153 | 154 | # header parameters 155 | header_params = header_params or {} 156 | header_params.update(self.default_headers) 157 | if self.cookie: 158 | header_params["Cookie"] = self.cookie 159 | if header_params: 160 | header_params = self.sanitize_for_serialization(header_params) 161 | header_params = dict( 162 | self.parameters_to_tuples(header_params, collection_formats) 163 | ) 164 | 165 | # path parameters 166 | if path_params: 167 | path_params = self.sanitize_for_serialization(path_params) 168 | path_params = self.parameters_to_tuples(path_params, collection_formats) 169 | for k, v in path_params: 170 | # specified safe chars, encode everything 171 | resource_path = resource_path.replace( 172 | "{%s}" % k, quote(str(v), safe=config.safe_chars_for_path_param) 173 | ) 174 | 175 | # query parameters 176 | if query_params: 177 | query_params = self.sanitize_for_serialization(query_params) 178 | query_params = self.parameters_to_tuples(query_params, collection_formats) 179 | 180 | # post parameters 181 | if post_params or files: 182 | post_params = post_params if post_params else [] 183 | post_params = self.sanitize_for_serialization(post_params) 184 | post_params = self.parameters_to_tuples(post_params, collection_formats) 185 | post_params.extend(self.files_parameters(files)) 186 | if header_params["Content-Type"].startswith("multipart"): 187 | post_params = self.parameters_to_multipart(post_params, (dict)) 188 | 189 | # body 190 | if body: 191 | body = self.sanitize_for_serialization(body) 192 | 193 | # auth setting 194 | self.update_params_for_auth( 195 | header_params, query_params, auth_settings, resource_path, method, body 196 | ) 197 | 198 | # request url 199 | if _host is None: 200 | url = self.configuration.host + resource_path 201 | else: 202 | # use server/host defined in path or operation instead 203 | url = _host + resource_path 204 | 205 | try: 206 | # perform request and return response 207 | response_data = self.request( 208 | method, 209 | url, 210 | query_params=query_params, 211 | headers=header_params, 212 | post_params=post_params, 213 | body=body, 214 | _preload_content=_preload_content, 215 | _request_timeout=_request_timeout, 216 | ) 217 | except ApiException as e: 218 | e.body = e.body.decode("utf-8") 219 | raise e 220 | 221 | self.last_response = response_data 222 | 223 | return_data = response_data 224 | 225 | if not _preload_content: 226 | return return_data 227 | return return_data 228 | 229 | # deserialize response data 230 | if response_type: 231 | if response_type != (file_type,): 232 | encoding = "utf-8" 233 | content_type = response_data.getheader("content-type") 234 | if content_type is not None: 235 | match = re.search(r"charset=([a-zA-Z\-\d]+)[\s\;]?", content_type) 236 | if match: 237 | encoding = match.group(1) 238 | response_data.data = response_data.data.decode(encoding) 239 | 240 | return_data = self.deserialize(response_data, response_type, _check_type) 241 | else: 242 | return_data = None 243 | 244 | if _return_http_data_only: 245 | return return_data 246 | else: 247 | return (return_data, response_data.status, response_data.getheaders()) 248 | 249 | def parameters_to_multipart(self, params, collection_types): 250 | """Get parameters as list of tuples, formatting as json if value is collection_types 251 | 252 | :param params: Parameters as list of two-tuples 253 | :param dict collection_types: Parameter collection types 254 | :return: Parameters as list of tuple or urllib3.fields.RequestField 255 | """ 256 | new_params = [] 257 | if collection_types is None: 258 | collection_types = dict 259 | for k, v in ( 260 | params.items() if isinstance(params, dict) else params 261 | ): # noqa: E501 262 | if isinstance( 263 | v, collection_types 264 | ): # v is instance of collection_type, formatting as application/json 265 | v = json.dumps(v, ensure_ascii=False).encode("utf-8") 266 | field = RequestField(k, v) 267 | field.make_multipart(content_type="application/json; charset=utf-8") 268 | new_params.append(field) 269 | else: 270 | new_params.append((k, v)) 271 | return new_params 272 | 273 | @classmethod 274 | def sanitize_for_serialization(cls, obj): 275 | """Prepares data for transmission before it is sent with the rest client 276 | If obj is None, return None. 277 | If obj is str, int, long, float, bool, return directly. 278 | If obj is datetime.datetime, datetime.date 279 | convert to string in iso8601 format. 280 | If obj is list, sanitize each element in the list. 281 | If obj is dict, return the dict. 282 | If obj is OpenAPI model, return the properties dict. 283 | If obj is io.IOBase, return the bytes 284 | :param obj: The data to serialize. 285 | :return: The serialized form of data. 286 | """ 287 | if isinstance(obj, (ModelNormal, ModelComposed)): 288 | return { 289 | key: cls.sanitize_for_serialization(val) 290 | for key, val in model_to_dict(obj, serialize=True).items() 291 | } 292 | elif isinstance(obj, io.IOBase): 293 | return cls.get_file_data_and_close_file(obj) 294 | elif isinstance(obj, (str, int, float, none_type, bool)): 295 | return obj 296 | elif isinstance(obj, (datetime, date)): 297 | return obj.isoformat() 298 | elif isinstance(obj, ModelSimple): 299 | return cls.sanitize_for_serialization(obj.value) 300 | elif isinstance(obj, (list, tuple)): 301 | return [cls.sanitize_for_serialization(item) for item in obj] 302 | if isinstance(obj, dict): 303 | return { 304 | key: cls.sanitize_for_serialization(val) for key, val in obj.items() 305 | } 306 | raise ApiValueError( 307 | "Unable to prepare type {} for serialization".format(obj.__class__.__name__) 308 | ) 309 | 310 | def deserialize(self, response, response_type, _check_type): 311 | """Deserializes response into an object. 312 | 313 | :param response: RESTResponse object to be deserialized. 314 | :param response_type: For the response, a tuple containing: 315 | valid classes 316 | a list containing valid classes (for list schemas) 317 | a dict containing a tuple of valid classes as the value 318 | Example values: 319 | (str,) 320 | (Pet,) 321 | (float, none_type) 322 | ([int, none_type],) 323 | ({str: (bool, str, int, float, date, datetime, str, none_type)},) 324 | :param _check_type: boolean, whether to check the types of the data 325 | received from the server 326 | :type _check_type: bool 327 | 328 | :return: deserialized object. 329 | """ 330 | # handle file downloading 331 | # save response body into a tmp file and return the instance 332 | if response_type == (file_type,): 333 | content_disposition = response.getheader("Content-Disposition") 334 | return deserialize_file( 335 | response.data, 336 | self.configuration, 337 | content_disposition=content_disposition, 338 | ) 339 | 340 | # fetch data from response object 341 | try: 342 | received_data = json.loads(response.data) 343 | except ValueError: 344 | received_data = response.data 345 | 346 | # store our data under the key of 'received_data' so users have some 347 | # context if they are deserializing a string and the data type is wrong 348 | deserialized_data = validate_and_convert_types( 349 | received_data, 350 | response_type, 351 | ["received_data"], 352 | True, 353 | _check_type, 354 | configuration=self.configuration, 355 | ) 356 | return deserialized_data 357 | 358 | def call_api( 359 | self, 360 | resource_path: str, 361 | method: str, 362 | path_params: typing.Optional[typing.Dict[str, typing.Any]] = None, 363 | query_params: typing.Optional[ 364 | typing.List[typing.Tuple[str, typing.Any]] 365 | ] = None, 366 | header_params: typing.Optional[typing.Dict[str, typing.Any]] = None, 367 | body: typing.Optional[typing.Any] = None, 368 | post_params: typing.Optional[typing.List[typing.Tuple[str, typing.Any]]] = None, 369 | files: typing.Optional[typing.Dict[str, typing.List[io.IOBase]]] = None, 370 | response_type: typing.Optional[typing.Tuple[typing.Any]] = None, 371 | auth_settings: typing.Optional[typing.List[str]] = None, 372 | async_req: typing.Optional[bool] = None, 373 | _return_http_data_only: typing.Optional[bool] = None, 374 | collection_formats: typing.Optional[typing.Dict[str, str]] = None, 375 | _preload_content: bool = True, 376 | _request_timeout: typing.Optional[ 377 | typing.Union[int, float, typing.Tuple] 378 | ] = None, 379 | _host: typing.Optional[str] = None, 380 | _check_type: typing.Optional[bool] = None, 381 | ): 382 | """Makes the HTTP request (synchronous) and returns deserialized data. 383 | 384 | To make an async_req request, set the async_req parameter. 385 | 386 | :param resource_path: Path to method endpoint. 387 | :param method: Method to call. 388 | :param path_params: Path parameters in the url. 389 | :param query_params: Query parameters in the url. 390 | :param header_params: Header parameters to be 391 | placed in the request header. 392 | :param body: Request body. 393 | :param post_params dict: Request post form parameters, 394 | for `application/x-www-form-urlencoded`, `multipart/form-data`. 395 | :param auth_settings list: Auth Settings names for the request. 396 | :param response_type: For the response, a tuple containing: 397 | valid classes 398 | a list containing valid classes (for list schemas) 399 | a dict containing a tuple of valid classes as the value 400 | Example values: 401 | (str,) 402 | (Pet,) 403 | (float, none_type) 404 | ([int, none_type],) 405 | ({str: (bool, str, int, float, date, datetime, str, none_type)},) 406 | :param files: key -> field name, value -> a list of open file 407 | objects for `multipart/form-data`. 408 | :type files: dict 409 | :param async_req bool: execute request asynchronously 410 | :type async_req: bool, optional 411 | :param _return_http_data_only: response data without head status code 412 | and headers 413 | :type _return_http_data_only: bool, optional 414 | :param collection_formats: dict of collection formats for path, query, 415 | header, and post parameters. 416 | :type collection_formats: dict, optional 417 | :param _preload_content: if False, the urllib3.HTTPResponse object will 418 | be returned without reading/decoding response 419 | data. Default is True. 420 | :type _preload_content: bool, optional 421 | :param _request_timeout: timeout setting for this request. If one 422 | number provided, it will be total request 423 | timeout. It can also be a pair (tuple) of 424 | (connection, read) timeouts. 425 | :param _check_type: boolean describing if the data back from the server 426 | should have its type checked. 427 | :type _check_type: bool, optional 428 | :return: 429 | If async_req parameter is True, 430 | the request will be called asynchronously. 431 | The method will return the request thread. 432 | If parameter async_req is False or missing, 433 | then the method will return the response directly. 434 | """ 435 | if not async_req: 436 | return self.__call_api( 437 | resource_path, 438 | method, 439 | path_params, 440 | query_params, 441 | header_params, 442 | body, 443 | post_params, 444 | files, 445 | response_type, 446 | auth_settings, 447 | _return_http_data_only, 448 | collection_formats, 449 | _preload_content, 450 | _request_timeout, 451 | _host, 452 | _check_type, 453 | ) 454 | 455 | return self.pool.apply_async( 456 | self.__call_api, 457 | ( 458 | resource_path, 459 | method, 460 | path_params, 461 | query_params, 462 | header_params, 463 | body, 464 | post_params, 465 | files, 466 | response_type, 467 | auth_settings, 468 | _return_http_data_only, 469 | collection_formats, 470 | _preload_content, 471 | _request_timeout, 472 | _host, 473 | _check_type, 474 | ), 475 | ) 476 | 477 | def request( 478 | self, 479 | method, 480 | url, 481 | query_params=None, 482 | headers=None, 483 | post_params=None, 484 | body=None, 485 | _preload_content=True, 486 | _request_timeout=None, 487 | ): 488 | """Makes the HTTP request using RESTClient.""" 489 | if method == "GET": 490 | return self.rest_client.GET( 491 | url, 492 | query_params=query_params, 493 | _preload_content=_preload_content, 494 | _request_timeout=_request_timeout, 495 | headers=headers, 496 | ) 497 | elif method == "HEAD": 498 | return self.rest_client.HEAD( 499 | url, 500 | query_params=query_params, 501 | _preload_content=_preload_content, 502 | _request_timeout=_request_timeout, 503 | headers=headers, 504 | ) 505 | elif method == "OPTIONS": 506 | return self.rest_client.OPTIONS( 507 | url, 508 | query_params=query_params, 509 | headers=headers, 510 | post_params=post_params, 511 | _preload_content=_preload_content, 512 | _request_timeout=_request_timeout, 513 | body=body, 514 | ) 515 | elif method == "POST": 516 | return self.rest_client.POST( 517 | url, 518 | query_params=query_params, 519 | headers=headers, 520 | post_params=post_params, 521 | _preload_content=_preload_content, 522 | _request_timeout=_request_timeout, 523 | body=body, 524 | ) 525 | elif method == "PUT": 526 | return self.rest_client.PUT( 527 | url, 528 | query_params=query_params, 529 | headers=headers, 530 | post_params=post_params, 531 | _preload_content=_preload_content, 532 | _request_timeout=_request_timeout, 533 | body=body, 534 | ) 535 | elif method == "PATCH": 536 | return self.rest_client.PATCH( 537 | url, 538 | query_params=query_params, 539 | headers=headers, 540 | post_params=post_params, 541 | _preload_content=_preload_content, 542 | _request_timeout=_request_timeout, 543 | body=body, 544 | ) 545 | elif method == "DELETE": 546 | return self.rest_client.DELETE( 547 | url, 548 | query_params=query_params, 549 | headers=headers, 550 | _preload_content=_preload_content, 551 | _request_timeout=_request_timeout, 552 | body=body, 553 | ) 554 | else: 555 | raise ApiValueError( 556 | "http method must be `GET`, `HEAD`, `OPTIONS`," 557 | " `POST`, `PATCH`, `PUT` or `DELETE`." 558 | ) 559 | 560 | def parameters_to_tuples(self, params, collection_formats): 561 | """Get parameters as list of tuples, formatting collections. 562 | 563 | :param params: Parameters as dict or list of two-tuples 564 | :param dict collection_formats: Parameter collection formats 565 | :return: Parameters as list of tuples, collections formatted 566 | """ 567 | new_params = [] 568 | if collection_formats is None: 569 | collection_formats = {} 570 | for k, v in ( 571 | params.items() if isinstance(params, dict) else params 572 | ): # noqa: E501 573 | if k in collection_formats: 574 | collection_format = collection_formats[k] 575 | if collection_format == "multi": 576 | new_params.extend((k, value) for value in v) 577 | else: 578 | if collection_format == "ssv": 579 | delimiter = " " 580 | elif collection_format == "tsv": 581 | delimiter = "\t" 582 | elif collection_format == "pipes": 583 | delimiter = "|" 584 | else: # csv is the default 585 | delimiter = "," 586 | new_params.append((k, delimiter.join(str(value) for value in v))) 587 | else: 588 | new_params.append((k, v)) 589 | return new_params 590 | 591 | @staticmethod 592 | def get_file_data_and_close_file(file_instance: io.IOBase) -> bytes: 593 | file_data = file_instance.read() 594 | file_instance.close() 595 | return file_data 596 | 597 | def files_parameters( 598 | self, files: typing.Optional[typing.Dict[str, typing.List[io.IOBase]]] = None 599 | ): 600 | """Builds form parameters. 601 | 602 | :param files: None or a dict with key=param_name and 603 | value is a list of open file objects 604 | :return: List of tuples of form parameters with file data 605 | """ 606 | if files is None: 607 | return [] 608 | 609 | params = [] 610 | for param_name, file_instances in files.items(): 611 | if file_instances is None: 612 | # if the file field is nullable, skip None values 613 | continue 614 | for file_instance in file_instances: 615 | if file_instance is None: 616 | # if the file field is nullable, skip None values 617 | continue 618 | if file_instance.closed is True: 619 | raise ApiValueError( 620 | "Cannot read a closed file. The passed in file_type " 621 | "for %s must be open." % param_name 622 | ) 623 | filename = os.path.basename(file_instance.name) 624 | filedata = self.get_file_data_and_close_file(file_instance) 625 | mimetype = ( 626 | mimetypes.guess_type(filename)[0] or "application/octet-stream" 627 | ) 628 | params.append( 629 | tuple([param_name, tuple([filename, filedata, mimetype])]) 630 | ) 631 | 632 | return params 633 | 634 | def select_header_accept(self, accepts): 635 | """Returns `Accept` based on an array of accepts provided. 636 | 637 | :param accepts: List of headers. 638 | :return: Accept (e.g. application/json). 639 | """ 640 | if not accepts: 641 | return 642 | 643 | accepts = [x.lower() for x in accepts] 644 | 645 | if "application/json" in accepts: 646 | return "application/json" 647 | else: 648 | return ", ".join(accepts) 649 | 650 | def select_header_content_type(self, content_types, method=None, body=None): 651 | """Returns `Content-Type` based on an array of content_types provided. 652 | 653 | :param content_types: List of content-types. 654 | :param method: http method (e.g. POST, PATCH). 655 | :param body: http body to send. 656 | :return: Content-Type (e.g. application/json). 657 | """ 658 | if not content_types: 659 | return "application/json" 660 | 661 | content_types = [x.lower() for x in content_types] 662 | 663 | if ( 664 | method == "PATCH" 665 | and "application/json-patch+json" in content_types 666 | and isinstance(body, list) 667 | ): 668 | return "application/json-patch+json" 669 | 670 | if "application/json" in content_types or "*/*" in content_types: 671 | return "application/json" 672 | else: 673 | return content_types[0] 674 | 675 | def update_params_for_auth( 676 | self, headers, queries, auth_settings, resource_path, method, body 677 | ): 678 | """Updates header and query params based on authentication setting. 679 | 680 | :param headers: Header parameters dict to be updated. 681 | :param queries: Query parameters tuple list to be updated. 682 | :param auth_settings: Authentication setting identifiers list. 683 | :param resource_path: A string representation of the HTTP request resource path. 684 | :param method: A string representation of the HTTP request method. 685 | :param body: A object representing the body of the HTTP request. 686 | The object type is the return value of _encoder.default(). 687 | """ 688 | if not auth_settings: 689 | return 690 | 691 | for auth in auth_settings: 692 | auth_setting = self.configuration.auth_settings().get(auth) 693 | if auth_setting: 694 | if auth_setting["in"] == "cookie": 695 | headers["Cookie"] = auth_setting["value"] 696 | elif auth_setting["in"] == "header": 697 | if auth_setting["type"] != "http-signature": 698 | headers[auth_setting["key"]] = auth_setting["value"] 699 | elif auth_setting["in"] == "query": 700 | queries.append((auth_setting["key"], auth_setting["value"])) 701 | else: 702 | raise ApiValueError( 703 | "Authentication token must be in `query` or `header`" 704 | ) 705 | 706 | 707 | class Endpoint(object): 708 | def __init__( 709 | self, 710 | settings=None, 711 | params_map=None, 712 | root_map=None, 713 | headers_map=None, 714 | api_client=None, 715 | callable=None, 716 | ): 717 | """Creates an endpoint 718 | 719 | Args: 720 | settings (dict): see below key value pairs 721 | 'response_type' (tuple/None): response type 722 | 'auth' (list): a list of auth type keys 723 | 'endpoint_path' (str): the endpoint path 724 | 'operation_id' (str): endpoint string identifier 725 | 'http_method' (str): POST/PUT/PATCH/GET etc 726 | 'servers' (list): list of str servers that this endpoint is at 727 | params_map (dict): see below key value pairs 728 | 'all' (list): list of str endpoint parameter names 729 | 'required' (list): list of required parameter names 730 | 'nullable' (list): list of nullable parameter names 731 | 'enum' (list): list of parameters with enum values 732 | 'validation' (list): list of parameters with validations 733 | root_map 734 | 'validations' (dict): the dict mapping endpoint parameter tuple 735 | paths to their validation dictionaries 736 | 'allowed_values' (dict): the dict mapping endpoint parameter 737 | tuple paths to their allowed_values (enum) dictionaries 738 | 'openapi_types' (dict): param_name to openapi type 739 | 'attribute_map' (dict): param_name to camelCase name 740 | 'location_map' (dict): param_name to 'body', 'file', 'form', 741 | 'header', 'path', 'query' 742 | collection_format_map (dict): param_name to `csv` etc. 743 | headers_map (dict): see below key value pairs 744 | 'accept' (list): list of Accept header strings 745 | 'content_type' (list): list of Content-Type header strings 746 | api_client (ApiClient) api client instance 747 | callable (function): the function which is invoked when the 748 | Endpoint is called 749 | """ 750 | self.settings = settings 751 | self.params_map = params_map 752 | self.params_map["all"].extend( 753 | [ 754 | "async_req", 755 | "_host_index", 756 | "_preload_content", 757 | "_request_timeout", 758 | "_return_http_data_only", 759 | "_check_input_type", 760 | "_check_return_type", 761 | "_content_type", 762 | "_spec_property_naming", 763 | ] 764 | ) 765 | self.params_map["nullable"].extend(["_request_timeout"]) 766 | self.validations = root_map["validations"] 767 | self.allowed_values = root_map["allowed_values"] 768 | self.openapi_types = root_map["openapi_types"] 769 | extra_types = { 770 | "async_req": (bool,), 771 | "_host_index": (none_type, int), 772 | "_preload_content": (bool,), 773 | "_request_timeout": ( 774 | none_type, 775 | float, 776 | (float,), 777 | [float], 778 | int, 779 | (int,), 780 | [int], 781 | ), 782 | "_return_http_data_only": (bool,), 783 | "_check_input_type": (bool,), 784 | "_check_return_type": (bool,), 785 | "_spec_property_naming": (bool,), 786 | "_content_type": (none_type, str), 787 | } 788 | self.openapi_types.update(extra_types) 789 | self.attribute_map = root_map["attribute_map"] 790 | self.location_map = root_map["location_map"] 791 | self.collection_format_map = root_map["collection_format_map"] 792 | self.headers_map = headers_map 793 | self.api_client = api_client 794 | self.callable = callable 795 | 796 | def __validate_inputs(self, kwargs): 797 | for param in self.params_map["enum"]: 798 | if param in kwargs: 799 | check_allowed_values(self.allowed_values, (param,), kwargs[param]) 800 | 801 | for param in self.params_map["validation"]: 802 | if param in kwargs: 803 | check_validations( 804 | self.validations, 805 | (param,), 806 | kwargs[param], 807 | configuration=self.api_client.configuration, 808 | ) 809 | 810 | if kwargs["_check_input_type"] is False: 811 | return 812 | 813 | for key, value in kwargs.items(): 814 | fixed_val = validate_and_convert_types( 815 | value, 816 | self.openapi_types[key], 817 | [key], 818 | kwargs["_spec_property_naming"], 819 | kwargs["_check_input_type"], 820 | configuration=self.api_client.configuration, 821 | ) 822 | kwargs[key] = fixed_val 823 | 824 | def __gather_params(self, kwargs): 825 | params = { 826 | "body": None, 827 | "collection_format": {}, 828 | "file": {}, 829 | "form": [], 830 | "header": {}, 831 | "path": {}, 832 | "query": [], 833 | } 834 | 835 | for param_name, param_value in kwargs.items(): 836 | param_location = self.location_map.get(param_name) 837 | if param_location is None: 838 | continue 839 | if param_location: 840 | if param_location == "body": 841 | params["body"] = param_value 842 | continue 843 | base_name = self.attribute_map[param_name] 844 | if param_location == "form" and self.openapi_types[param_name] == ( 845 | file_type, 846 | ): 847 | params["file"][base_name] = [param_value] 848 | elif param_location == "form" and self.openapi_types[param_name] == ( 849 | [file_type], 850 | ): 851 | # param_value is already a list 852 | params["file"][base_name] = param_value 853 | elif param_location in {"form", "query"}: 854 | param_value_full = (base_name, param_value) 855 | params[param_location].append(param_value_full) 856 | if param_location not in {"form", "query"}: 857 | params[param_location][base_name] = param_value 858 | collection_format = self.collection_format_map.get(param_name) 859 | if collection_format: 860 | params["collection_format"][base_name] = collection_format 861 | 862 | return params 863 | 864 | def __call__(self, *args, **kwargs): 865 | """This method is invoked when endpoints are called 866 | Example: 867 | 868 | api_instance = DefaultApi() 869 | api_instance.risikogebiete_get # this is an instance of the class Endpoint 870 | api_instance.risikogebiete_get() # this invokes api_instance.risikogebiete_get.__call__() 871 | which then invokes the callable functions stored in that endpoint at 872 | api_instance.risikogebiete_get.callable or self.callable in this class 873 | 874 | """ 875 | return self.callable(self, *args, **kwargs) 876 | 877 | def call_with_http_info(self, **kwargs): 878 | 879 | try: 880 | index = ( 881 | self.api_client.configuration.server_operation_index.get( 882 | self.settings["operation_id"], 883 | self.api_client.configuration.server_index, 884 | ) 885 | if kwargs["_host_index"] is None 886 | else kwargs["_host_index"] 887 | ) 888 | server_variables = ( 889 | self.api_client.configuration.server_operation_variables.get( 890 | self.settings["operation_id"], 891 | self.api_client.configuration.server_variables, 892 | ) 893 | ) 894 | _host = self.api_client.configuration.get_host_from_settings( 895 | index, variables=server_variables, servers=self.settings["servers"] 896 | ) 897 | except IndexError: 898 | if self.settings["servers"]: 899 | raise ApiValueError( 900 | "Invalid host index. Must be 0 <= index < %s" 901 | % len(self.settings["servers"]) 902 | ) 903 | _host = None 904 | 905 | for key, value in kwargs.items(): 906 | if key not in self.params_map["all"]: 907 | raise ApiTypeError( 908 | "Got an unexpected parameter '%s'" 909 | " to method `%s`" % (key, self.settings["operation_id"]) 910 | ) 911 | # only throw this nullable ApiValueError if _check_input_type 912 | # is False, if _check_input_type==True we catch this case 913 | # in self.__validate_inputs 914 | if ( 915 | key not in self.params_map["nullable"] 916 | and value is None 917 | and kwargs["_check_input_type"] is False 918 | ): 919 | raise ApiValueError( 920 | "Value may not be None for non-nullable parameter `%s`" 921 | " when calling `%s`" % (key, self.settings["operation_id"]) 922 | ) 923 | 924 | for key in self.params_map["required"]: 925 | if key not in kwargs.keys(): 926 | raise ApiValueError( 927 | "Missing the required parameter `%s` when calling " 928 | "`%s`" % (key, self.settings["operation_id"]) 929 | ) 930 | 931 | self.__validate_inputs(kwargs) 932 | 933 | params = self.__gather_params(kwargs) 934 | 935 | accept_headers_list = self.headers_map["accept"] 936 | if accept_headers_list: 937 | params["header"]["Accept"] = self.api_client.select_header_accept( 938 | accept_headers_list 939 | ) 940 | 941 | if kwargs.get("_content_type"): 942 | params["header"]["Content-Type"] = kwargs["_content_type"] 943 | else: 944 | content_type_headers_list = self.headers_map["content_type"] 945 | if content_type_headers_list: 946 | if params["body"] != "": 947 | header_list = self.api_client.select_header_content_type( 948 | content_type_headers_list, 949 | self.settings["http_method"], 950 | params["body"], 951 | ) 952 | params["header"]["Content-Type"] = header_list 953 | 954 | return self.api_client.call_api( 955 | self.settings["endpoint_path"], 956 | self.settings["http_method"], 957 | params["path"], 958 | params["query"], 959 | params["header"], 960 | body=params["body"], 961 | post_params=params["form"], 962 | files=params["file"], 963 | response_type=self.settings["response_type"], 964 | auth_settings=self.settings["auth"], 965 | async_req=kwargs["async_req"], 966 | _check_type=kwargs["_check_return_type"], 967 | _return_http_data_only=kwargs["_return_http_data_only"], 968 | _preload_content=kwargs["_preload_content"], 969 | _request_timeout=kwargs["_request_timeout"], 970 | _host=_host, 971 | collection_formats=params["collection_format"], 972 | ) 973 | -------------------------------------------------------------------------------- /python-client/deutschland/risikogebiete/apis/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | # Import all APIs into this package. 4 | # If you have many APIs here with many many models used in each API this may 5 | # raise a `RecursionError`. 6 | # In order to avoid this, import only the API that you directly need like: 7 | # 8 | # from .api.default_api import DefaultApi 9 | # 10 | # or import this package, but before doing it, use: 11 | # 12 | # import sys 13 | # sys.setrecursionlimit(n) 14 | 15 | # Import APIs into API package: 16 | from deutschland.risikogebiete.api.default_api import DefaultApi 17 | -------------------------------------------------------------------------------- /python-client/deutschland/risikogebiete/configuration.py: -------------------------------------------------------------------------------- 1 | """ 2 | Robert Koch Institut: Corona Risikogebiete API 3 | 4 | Aktuelle Corona Risikogebietsinformationen als API # noqa: E501 5 | 6 | The version of the OpenAPI document: 1.0.0 7 | Contact: kontakt@bund.dev 8 | Generated by: https://openapi-generator.tech 9 | """ 10 | 11 | 12 | import copy 13 | import logging 14 | import multiprocessing 15 | import sys 16 | from http import client as http_client 17 | 18 | import urllib3 19 | from deutschland.risikogebiete.exceptions import ApiValueError 20 | 21 | JSON_SCHEMA_VALIDATION_KEYWORDS = { 22 | "multipleOf", 23 | "maximum", 24 | "exclusiveMaximum", 25 | "minimum", 26 | "exclusiveMinimum", 27 | "maxLength", 28 | "minLength", 29 | "pattern", 30 | "maxItems", 31 | "minItems", 32 | } 33 | 34 | 35 | class Configuration(object): 36 | """NOTE: This class is auto generated by OpenAPI Generator 37 | 38 | Ref: https://openapi-generator.tech 39 | Do not edit the class manually. 40 | 41 | :param host: Base url 42 | :param api_key: Dict to store API key(s). 43 | Each entry in the dict specifies an API key. 44 | The dict key is the name of the security scheme in the OAS specification. 45 | The dict value is the API key secret. 46 | :param api_key_prefix: Dict to store API prefix (e.g. Bearer) 47 | The dict key is the name of the security scheme in the OAS specification. 48 | The dict value is an API key prefix when generating the auth data. 49 | :param username: Username for HTTP basic authentication 50 | :param password: Password for HTTP basic authentication 51 | :param discard_unknown_keys: Boolean value indicating whether to discard 52 | unknown properties. A server may send a response that includes additional 53 | properties that are not known by the client in the following scenarios: 54 | 1. The OpenAPI document is incomplete, i.e. it does not match the server 55 | implementation. 56 | 2. The client was generated using an older version of the OpenAPI document 57 | and the server has been upgraded since then. 58 | If a schema in the OpenAPI document defines the additionalProperties attribute, 59 | then all undeclared properties received by the server are injected into the 60 | additional properties map. In that case, there are undeclared properties, and 61 | nothing to discard. 62 | :param disabled_client_side_validations (string): Comma-separated list of 63 | JSON schema validation keywords to disable JSON schema structural validation 64 | rules. The following keywords may be specified: multipleOf, maximum, 65 | exclusiveMaximum, minimum, exclusiveMinimum, maxLength, minLength, pattern, 66 | maxItems, minItems. 67 | By default, the validation is performed for data generated locally by the client 68 | and data received from the server, independent of any validation performed by 69 | the server side. If the input data does not satisfy the JSON schema validation 70 | rules specified in the OpenAPI document, an exception is raised. 71 | If disabled_client_side_validations is set, structural validation is 72 | disabled. This can be useful to troubleshoot data validation problem, such as 73 | when the OpenAPI document validation rules do not match the actual API data 74 | received by the server. 75 | :param server_index: Index to servers configuration. 76 | :param server_variables: Mapping with string values to replace variables in 77 | templated server configuration. The validation of enums is performed for 78 | variables with defined enum values before. 79 | :param server_operation_index: Mapping from operation ID to an index to server 80 | configuration. 81 | :param server_operation_variables: Mapping from operation ID to a mapping with 82 | string values to replace variables in templated server configuration. 83 | The validation of enums is performed for variables with defined enum values before. 84 | :param ssl_ca_cert: str - the path to a file of concatenated CA certificates 85 | in PEM format 86 | 87 | """ 88 | 89 | _default = None 90 | 91 | def __init__( 92 | self, 93 | host=None, 94 | api_key=None, 95 | api_key_prefix=None, 96 | access_token=None, 97 | username=None, 98 | password=None, 99 | discard_unknown_keys=False, 100 | disabled_client_side_validations="", 101 | server_index=None, 102 | server_variables=None, 103 | server_operation_index=None, 104 | server_operation_variables=None, 105 | ssl_ca_cert=None, 106 | ): 107 | """Constructor""" 108 | self._base_path = ( 109 | "https://api.einreiseanmeldung.de/reisendenportal" if host is None else host 110 | ) 111 | """Default Base url 112 | """ 113 | self.server_index = 0 if server_index is None and host is None else server_index 114 | self.server_operation_index = server_operation_index or {} 115 | """Default server index 116 | """ 117 | self.server_variables = server_variables or {} 118 | self.server_operation_variables = server_operation_variables or {} 119 | """Default server variables 120 | """ 121 | self.temp_folder_path = None 122 | """Temp file folder for downloading files 123 | """ 124 | # Authentication Settings 125 | self.access_token = access_token 126 | self.api_key = {} 127 | if api_key: 128 | self.api_key = api_key 129 | """dict to store API key(s) 130 | """ 131 | self.api_key_prefix = {} 132 | if api_key_prefix: 133 | self.api_key_prefix = api_key_prefix 134 | """dict to store API prefix (e.g. Bearer) 135 | """ 136 | self.refresh_api_key_hook = None 137 | """function hook to refresh API key if expired 138 | """ 139 | self.username = username 140 | """Username for HTTP basic authentication 141 | """ 142 | self.password = password 143 | """Password for HTTP basic authentication 144 | """ 145 | self.discard_unknown_keys = discard_unknown_keys 146 | self.disabled_client_side_validations = disabled_client_side_validations 147 | self.logger = {} 148 | """Logging Settings 149 | """ 150 | self.logger["package_logger"] = logging.getLogger("risikogebiete") 151 | self.logger["urllib3_logger"] = logging.getLogger("urllib3") 152 | self.logger_format = "%(asctime)s %(levelname)s %(message)s" 153 | """Log format 154 | """ 155 | self.logger_stream_handler = None 156 | """Log stream handler 157 | """ 158 | self.logger_file_handler = None 159 | """Log file handler 160 | """ 161 | self.logger_file = None 162 | """Debug file location 163 | """ 164 | self.debug = False 165 | """Debug switch 166 | """ 167 | 168 | self.verify_ssl = True 169 | """SSL/TLS verification 170 | Set this to false to skip verifying SSL certificate when calling API 171 | from https server. 172 | """ 173 | self.ssl_ca_cert = ssl_ca_cert 174 | """Set this to customize the certificate file to verify the peer. 175 | """ 176 | self.cert_file = None 177 | """client certificate file 178 | """ 179 | self.key_file = None 180 | """client key file 181 | """ 182 | self.assert_hostname = None 183 | """Set this to True/False to enable/disable SSL hostname verification. 184 | """ 185 | 186 | self.connection_pool_maxsize = multiprocessing.cpu_count() * 5 187 | """urllib3 connection pool's maximum number of connections saved 188 | per pool. urllib3 uses 1 connection as default value, but this is 189 | not the best value when you are making a lot of possibly parallel 190 | requests to the same host, which is often the case here. 191 | cpu_count * 5 is used as default value to increase performance. 192 | """ 193 | 194 | self.proxy = None 195 | """Proxy URL 196 | """ 197 | self.no_proxy = None 198 | """bypass proxy for host in the no_proxy list. 199 | """ 200 | self.proxy_headers = None 201 | """Proxy headers 202 | """ 203 | self.safe_chars_for_path_param = "" 204 | """Safe chars for path_param 205 | """ 206 | self.retries = None 207 | """Adding retries to override urllib3 default value 3 208 | """ 209 | # Enable client side validation 210 | self.client_side_validation = True 211 | 212 | # Options to pass down to the underlying urllib3 socket 213 | self.socket_options = None 214 | 215 | def __deepcopy__(self, memo): 216 | cls = self.__class__ 217 | result = cls.__new__(cls) 218 | memo[id(self)] = result 219 | for k, v in self.__dict__.items(): 220 | if k not in ("logger", "logger_file_handler"): 221 | setattr(result, k, copy.deepcopy(v, memo)) 222 | # shallow copy of loggers 223 | result.logger = copy.copy(self.logger) 224 | # use setters to configure loggers 225 | result.logger_file = self.logger_file 226 | result.debug = self.debug 227 | return result 228 | 229 | def __setattr__(self, name, value): 230 | object.__setattr__(self, name, value) 231 | if name == "disabled_client_side_validations": 232 | s = set(filter(None, value.split(","))) 233 | for v in s: 234 | if v not in JSON_SCHEMA_VALIDATION_KEYWORDS: 235 | raise ApiValueError("Invalid keyword: '{0}''".format(v)) 236 | self._disabled_client_side_validations = s 237 | 238 | @classmethod 239 | def set_default(cls, default): 240 | """Set default instance of configuration. 241 | 242 | It stores default configuration, which can be 243 | returned by get_default_copy method. 244 | 245 | :param default: object of Configuration 246 | """ 247 | cls._default = copy.deepcopy(default) 248 | 249 | @classmethod 250 | def get_default_copy(cls): 251 | """Return new instance of configuration. 252 | 253 | This method returns newly created, based on default constructor, 254 | object of Configuration class or returns a copy of default 255 | configuration passed by the set_default method. 256 | 257 | :return: The configuration object. 258 | """ 259 | if cls._default is not None: 260 | return copy.deepcopy(cls._default) 261 | return Configuration() 262 | 263 | @property 264 | def logger_file(self): 265 | """The logger file. 266 | 267 | If the logger_file is None, then add stream handler and remove file 268 | handler. Otherwise, add file handler and remove stream handler. 269 | 270 | :param value: The logger_file path. 271 | :type: str 272 | """ 273 | return self.__logger_file 274 | 275 | @logger_file.setter 276 | def logger_file(self, value): 277 | """The logger file. 278 | 279 | If the logger_file is None, then add stream handler and remove file 280 | handler. Otherwise, add file handler and remove stream handler. 281 | 282 | :param value: The logger_file path. 283 | :type: str 284 | """ 285 | self.__logger_file = value 286 | if self.__logger_file: 287 | # If set logging file, 288 | # then add file handler and remove stream handler. 289 | self.logger_file_handler = logging.FileHandler(self.__logger_file) 290 | self.logger_file_handler.setFormatter(self.logger_formatter) 291 | for _, logger in self.logger.items(): 292 | logger.addHandler(self.logger_file_handler) 293 | 294 | @property 295 | def debug(self): 296 | """Debug status 297 | 298 | :param value: The debug status, True or False. 299 | :type: bool 300 | """ 301 | return self.__debug 302 | 303 | @debug.setter 304 | def debug(self, value): 305 | """Debug status 306 | 307 | :param value: The debug status, True or False. 308 | :type: bool 309 | """ 310 | self.__debug = value 311 | if self.__debug: 312 | # if debug status is True, turn on debug logging 313 | for _, logger in self.logger.items(): 314 | logger.setLevel(logging.DEBUG) 315 | # turn on http_client debug 316 | http_client.HTTPConnection.debuglevel = 1 317 | else: 318 | # if debug status is False, turn off debug logging, 319 | # setting log level to default `logging.WARNING` 320 | for _, logger in self.logger.items(): 321 | logger.setLevel(logging.WARNING) 322 | # turn off http_client debug 323 | http_client.HTTPConnection.debuglevel = 0 324 | 325 | @property 326 | def logger_format(self): 327 | """The logger format. 328 | 329 | The logger_formatter will be updated when sets logger_format. 330 | 331 | :param value: The format string. 332 | :type: str 333 | """ 334 | return self.__logger_format 335 | 336 | @logger_format.setter 337 | def logger_format(self, value): 338 | """The logger format. 339 | 340 | The logger_formatter will be updated when sets logger_format. 341 | 342 | :param value: The format string. 343 | :type: str 344 | """ 345 | self.__logger_format = value 346 | self.logger_formatter = logging.Formatter(self.__logger_format) 347 | 348 | def get_api_key_with_prefix(self, identifier, alias=None): 349 | """Gets API key (with prefix if set). 350 | 351 | :param identifier: The identifier of apiKey. 352 | :param alias: The alternative identifier of apiKey. 353 | :return: The token for api key authentication. 354 | """ 355 | if self.refresh_api_key_hook is not None: 356 | self.refresh_api_key_hook(self) 357 | key = self.api_key.get( 358 | identifier, self.api_key.get(alias) if alias is not None else None 359 | ) 360 | if key: 361 | prefix = self.api_key_prefix.get(identifier) 362 | if prefix: 363 | return "%s %s" % (prefix, key) 364 | else: 365 | return key 366 | 367 | def get_basic_auth_token(self): 368 | """Gets HTTP basic authentication header (string). 369 | 370 | :return: The token for basic HTTP authentication. 371 | """ 372 | username = "" 373 | if self.username is not None: 374 | username = self.username 375 | password = "" 376 | if self.password is not None: 377 | password = self.password 378 | return urllib3.util.make_headers(basic_auth=username + ":" + password).get( 379 | "authorization" 380 | ) 381 | 382 | def auth_settings(self): 383 | """Gets Auth Settings dict for api client. 384 | 385 | :return: The Auth Settings information dict. 386 | """ 387 | auth = {} 388 | return auth 389 | 390 | def to_debug_report(self): 391 | """Gets the essential information for debugging. 392 | 393 | :return: The report for debugging. 394 | """ 395 | return ( 396 | "Python SDK Debug Report:\n" 397 | "OS: {env}\n" 398 | "Python Version: {pyversion}\n" 399 | "Version of the API: 1.0.0\n" 400 | "SDK Package Version: 0.1.0".format(env=sys.platform, pyversion=sys.version) 401 | ) 402 | 403 | def get_host_settings(self): 404 | """Gets an array of host settings 405 | 406 | :return: An array of host settings 407 | """ 408 | return [ 409 | { 410 | "url": "https://api.einreiseanmeldung.de/reisendenportal", 411 | "description": "No description provided", 412 | } 413 | ] 414 | 415 | def get_host_from_settings(self, index, variables=None, servers=None): 416 | """Gets host URL based on the index and variables 417 | :param index: array index of the host settings 418 | :param variables: hash of variable and the corresponding value 419 | :param servers: an array of host settings or None 420 | :return: URL based on host settings 421 | """ 422 | if index is None: 423 | return self._base_path 424 | 425 | variables = {} if variables is None else variables 426 | servers = self.get_host_settings() if servers is None else servers 427 | 428 | try: 429 | server = servers[index] 430 | except IndexError: 431 | raise ValueError( 432 | "Invalid index {0} when selecting the host settings. " 433 | "Must be less than {1}".format(index, len(servers)) 434 | ) 435 | 436 | url = server["url"] 437 | 438 | # go through variables and replace placeholders 439 | for variable_name, variable in server.get("variables", {}).items(): 440 | used_value = variables.get(variable_name, variable["default_value"]) 441 | 442 | if "enum_values" in variable and used_value not in variable["enum_values"]: 443 | raise ValueError( 444 | "The variable `{0}` in the host URL has invalid value " 445 | "{1}. Must be {2}.".format( 446 | variable_name, variables[variable_name], variable["enum_values"] 447 | ) 448 | ) 449 | 450 | url = url.replace("{" + variable_name + "}", used_value) 451 | 452 | return url 453 | 454 | @property 455 | def host(self): 456 | """Return generated host.""" 457 | return self.get_host_from_settings( 458 | self.server_index, variables=self.server_variables 459 | ) 460 | 461 | @host.setter 462 | def host(self, value): 463 | """Fix base path.""" 464 | self._base_path = value 465 | self.server_index = None 466 | -------------------------------------------------------------------------------- /python-client/deutschland/risikogebiete/exceptions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Robert Koch Institut: Corona Risikogebiete API 3 | 4 | Aktuelle Corona Risikogebietsinformationen als API # noqa: E501 5 | 6 | The version of the OpenAPI document: 1.0.0 7 | Contact: kontakt@bund.dev 8 | Generated by: https://openapi-generator.tech 9 | """ 10 | 11 | 12 | class OpenApiException(Exception): 13 | """The base exception class for all OpenAPIExceptions""" 14 | 15 | 16 | class ApiTypeError(OpenApiException, TypeError): 17 | def __init__(self, msg, path_to_item=None, valid_classes=None, key_type=None): 18 | """Raises an exception for TypeErrors 19 | 20 | Args: 21 | msg (str): the exception message 22 | 23 | Keyword Args: 24 | path_to_item (list): a list of keys an indices to get to the 25 | current_item 26 | None if unset 27 | valid_classes (tuple): the primitive classes that current item 28 | should be an instance of 29 | None if unset 30 | key_type (bool): False if our value is a value in a dict 31 | True if it is a key in a dict 32 | False if our item is an item in a list 33 | None if unset 34 | """ 35 | self.path_to_item = path_to_item 36 | self.valid_classes = valid_classes 37 | self.key_type = key_type 38 | full_msg = msg 39 | if path_to_item: 40 | full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) 41 | super(ApiTypeError, self).__init__(full_msg) 42 | 43 | 44 | class ApiValueError(OpenApiException, ValueError): 45 | def __init__(self, msg, path_to_item=None): 46 | """ 47 | Args: 48 | msg (str): the exception message 49 | 50 | Keyword Args: 51 | path_to_item (list) the path to the exception in the 52 | received_data dict. None if unset 53 | """ 54 | 55 | self.path_to_item = path_to_item 56 | full_msg = msg 57 | if path_to_item: 58 | full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) 59 | super(ApiValueError, self).__init__(full_msg) 60 | 61 | 62 | class ApiAttributeError(OpenApiException, AttributeError): 63 | def __init__(self, msg, path_to_item=None): 64 | """ 65 | Raised when an attribute reference or assignment fails. 66 | 67 | Args: 68 | msg (str): the exception message 69 | 70 | Keyword Args: 71 | path_to_item (None/list) the path to the exception in the 72 | received_data dict 73 | """ 74 | self.path_to_item = path_to_item 75 | full_msg = msg 76 | if path_to_item: 77 | full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) 78 | super(ApiAttributeError, self).__init__(full_msg) 79 | 80 | 81 | class ApiKeyError(OpenApiException, KeyError): 82 | def __init__(self, msg, path_to_item=None): 83 | """ 84 | Args: 85 | msg (str): the exception message 86 | 87 | Keyword Args: 88 | path_to_item (None/list) the path to the exception in the 89 | received_data dict 90 | """ 91 | self.path_to_item = path_to_item 92 | full_msg = msg 93 | if path_to_item: 94 | full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) 95 | super(ApiKeyError, self).__init__(full_msg) 96 | 97 | 98 | class ApiException(OpenApiException): 99 | def __init__(self, status=None, reason=None, http_resp=None): 100 | if http_resp: 101 | self.status = http_resp.status 102 | self.reason = http_resp.reason 103 | self.body = http_resp.data 104 | self.headers = http_resp.getheaders() 105 | else: 106 | self.status = status 107 | self.reason = reason 108 | self.body = None 109 | self.headers = None 110 | 111 | def __str__(self): 112 | """Custom error messages for exception""" 113 | error_message = "({0})\n" "Reason: {1}\n".format(self.status, self.reason) 114 | if self.headers: 115 | error_message += "HTTP response headers: {0}\n".format(self.headers) 116 | 117 | if self.body: 118 | error_message += "HTTP response body: {0}\n".format(self.body) 119 | 120 | return error_message 121 | 122 | 123 | class NotFoundException(ApiException): 124 | def __init__(self, status=None, reason=None, http_resp=None): 125 | super(NotFoundException, self).__init__(status, reason, http_resp) 126 | 127 | 128 | class UnauthorizedException(ApiException): 129 | def __init__(self, status=None, reason=None, http_resp=None): 130 | super(UnauthorizedException, self).__init__(status, reason, http_resp) 131 | 132 | 133 | class ForbiddenException(ApiException): 134 | def __init__(self, status=None, reason=None, http_resp=None): 135 | super(ForbiddenException, self).__init__(status, reason, http_resp) 136 | 137 | 138 | class ServiceException(ApiException): 139 | def __init__(self, status=None, reason=None, http_resp=None): 140 | super(ServiceException, self).__init__(status, reason, http_resp) 141 | 142 | 143 | def render_path(path_to_item): 144 | """Returns a string representation of a path""" 145 | result = "" 146 | for pth in path_to_item: 147 | if isinstance(pth, int): 148 | result += "[{0}]".format(pth) 149 | else: 150 | result += "['{0}']".format(pth) 151 | return result 152 | -------------------------------------------------------------------------------- /python-client/deutschland/risikogebiete/model/__init__.py: -------------------------------------------------------------------------------- 1 | # we can not import model classes here because that would create a circular 2 | # reference which would not work in python2 3 | # do not import all models into this module because that uses a lot of memory and stack frames 4 | # if you need the ability to import all models from one package, import them with 5 | # from deutschland.risikogebiete.models import ModelA, ModelB 6 | -------------------------------------------------------------------------------- /python-client/deutschland/risikogebiete/model/risk_countries.py: -------------------------------------------------------------------------------- 1 | """ 2 | Robert Koch Institut: Corona Risikogebiete API 3 | 4 | Aktuelle Corona Risikogebietsinformationen als API # noqa: E501 5 | 6 | The version of the OpenAPI document: 1.0.0 7 | Contact: kontakt@bund.dev 8 | Generated by: https://openapi-generator.tech 9 | """ 10 | 11 | 12 | import re # noqa: F401 13 | import sys # noqa: F401 14 | 15 | from deutschland.risikogebiete.exceptions import ApiAttributeError 16 | from deutschland.risikogebiete.model_utils import ( # noqa: F401 17 | ApiTypeError, 18 | ModelComposed, 19 | ModelNormal, 20 | ModelSimple, 21 | OpenApiModel, 22 | cached_property, 23 | change_keys_js_to_python, 24 | convert_js_args_to_python_args, 25 | date, 26 | datetime, 27 | file_type, 28 | none_type, 29 | validate_get_composed_info, 30 | ) 31 | 32 | 33 | class RiskCountries(ModelSimple): 34 | """NOTE: This class is auto generated by OpenAPI Generator. 35 | Ref: https://openapi-generator.tech 36 | 37 | Do not edit the class manually. 38 | 39 | Attributes: 40 | allowed_values (dict): The key is the tuple path to the attribute 41 | and the for var_name this is (var_name,). The value is a dict 42 | with a capitalized key describing the allowed value and an allowed 43 | value. These dicts store the allowed enum values. 44 | validations (dict): The key is the tuple path to the attribute 45 | and the for var_name this is (var_name,). The value is a dict 46 | that stores validations for max_length, min_length, max_items, 47 | min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, 48 | inclusive_minimum, and regex. 49 | additional_properties_type (tuple): A tuple of classes accepted 50 | as additional properties values. 51 | """ 52 | 53 | allowed_values = {} 54 | 55 | validations = {} 56 | 57 | additional_properties_type = None 58 | 59 | _nullable = False 60 | 61 | @cached_property 62 | def openapi_types(): 63 | """ 64 | This must be a method because a model may have properties that are 65 | of type self, this must run after the class is loaded 66 | 67 | Returns 68 | openapi_types (dict): The key is attribute name 69 | and the value is attribute type. 70 | """ 71 | return { 72 | "value": ( 73 | [{str: (bool, date, datetime, dict, float, int, list, str, none_type)}], 74 | ), 75 | } 76 | 77 | @cached_property 78 | def discriminator(): 79 | return None 80 | 81 | attribute_map = {} 82 | 83 | read_only_vars = set() 84 | 85 | _composed_schemas = None 86 | 87 | required_properties = set( 88 | [ 89 | "_data_store", 90 | "_check_type", 91 | "_spec_property_naming", 92 | "_path_to_item", 93 | "_configuration", 94 | "_visited_composed_classes", 95 | ] 96 | ) 97 | 98 | @convert_js_args_to_python_args 99 | def __init__(self, *args, **kwargs): 100 | """RiskCountries - a model defined in OpenAPI 101 | 102 | Note that value can be passed either in args or in kwargs, but not in both. 103 | 104 | Args: 105 | args[0] ([{str: (bool, date, datetime, dict, float, int, list, str, none_type)}]): # noqa: E501 106 | 107 | Keyword Args: 108 | value ([{str: (bool, date, datetime, dict, float, int, list, str, none_type)}]): # noqa: E501 109 | _check_type (bool): if True, values for parameters in openapi_types 110 | will be type checked and a TypeError will be 111 | raised if the wrong type is input. 112 | Defaults to True 113 | _path_to_item (tuple/list): This is a list of keys or values to 114 | drill down to the model in received_data 115 | when deserializing a response 116 | _spec_property_naming (bool): True if the variable names in the input data 117 | are serialized names, as specified in the OpenAPI document. 118 | False if the variable names in the input data 119 | are pythonic names, e.g. snake case (default) 120 | _configuration (Configuration): the instance to use when 121 | deserializing a file_type parameter. 122 | If passed, type conversion is attempted 123 | If omitted no type conversion is done. 124 | _visited_composed_classes (tuple): This stores a tuple of 125 | classes that we have traveled through so that 126 | if we see that class again we will not use its 127 | discriminator again. 128 | When traveling through a discriminator, the 129 | composed schema that is 130 | is traveled through is added to this set. 131 | For example if Animal has a discriminator 132 | petType and we pass in "Dog", and the class Dog 133 | allOf includes Animal, we move through Animal 134 | once using the discriminator, and pick Dog. 135 | Then in Dog, we will make an instance of the 136 | Animal class but this time we won't travel 137 | through its discriminator because we passed in 138 | _visited_composed_classes = (Animal,) 139 | """ 140 | # required up here when default value is not given 141 | _path_to_item = kwargs.pop("_path_to_item", ()) 142 | 143 | if "value" in kwargs: 144 | value = kwargs.pop("value") 145 | elif args: 146 | args = list(args) 147 | value = args.pop(0) 148 | else: 149 | raise ApiTypeError( 150 | "value is required, but not passed in args or kwargs and doesn't have default", 151 | path_to_item=_path_to_item, 152 | valid_classes=(self.__class__,), 153 | ) 154 | 155 | _check_type = kwargs.pop("_check_type", True) 156 | _spec_property_naming = kwargs.pop("_spec_property_naming", False) 157 | _configuration = kwargs.pop("_configuration", None) 158 | _visited_composed_classes = kwargs.pop("_visited_composed_classes", ()) 159 | 160 | if args: 161 | raise ApiTypeError( 162 | "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." 163 | % ( 164 | args, 165 | self.__class__.__name__, 166 | ), 167 | path_to_item=_path_to_item, 168 | valid_classes=(self.__class__,), 169 | ) 170 | 171 | self._data_store = {} 172 | self._check_type = _check_type 173 | self._spec_property_naming = _spec_property_naming 174 | self._path_to_item = _path_to_item 175 | self._configuration = _configuration 176 | self._visited_composed_classes = _visited_composed_classes + (self.__class__,) 177 | self.value = value 178 | if kwargs: 179 | raise ApiTypeError( 180 | "Invalid named arguments=%s passed to %s. Remove those invalid named arguments." 181 | % ( 182 | kwargs, 183 | self.__class__.__name__, 184 | ), 185 | path_to_item=_path_to_item, 186 | valid_classes=(self.__class__,), 187 | ) 188 | 189 | @classmethod 190 | @convert_js_args_to_python_args 191 | def _from_openapi_data(cls, *args, **kwargs): 192 | """RiskCountries - a model defined in OpenAPI 193 | 194 | Note that value can be passed either in args or in kwargs, but not in both. 195 | 196 | Args: 197 | args[0] ([{str: (bool, date, datetime, dict, float, int, list, str, none_type)}]): # noqa: E501 198 | 199 | Keyword Args: 200 | value ([{str: (bool, date, datetime, dict, float, int, list, str, none_type)}]): # noqa: E501 201 | _check_type (bool): if True, values for parameters in openapi_types 202 | will be type checked and a TypeError will be 203 | raised if the wrong type is input. 204 | Defaults to True 205 | _path_to_item (tuple/list): This is a list of keys or values to 206 | drill down to the model in received_data 207 | when deserializing a response 208 | _spec_property_naming (bool): True if the variable names in the input data 209 | are serialized names, as specified in the OpenAPI document. 210 | False if the variable names in the input data 211 | are pythonic names, e.g. snake case (default) 212 | _configuration (Configuration): the instance to use when 213 | deserializing a file_type parameter. 214 | If passed, type conversion is attempted 215 | If omitted no type conversion is done. 216 | _visited_composed_classes (tuple): This stores a tuple of 217 | classes that we have traveled through so that 218 | if we see that class again we will not use its 219 | discriminator again. 220 | When traveling through a discriminator, the 221 | composed schema that is 222 | is traveled through is added to this set. 223 | For example if Animal has a discriminator 224 | petType and we pass in "Dog", and the class Dog 225 | allOf includes Animal, we move through Animal 226 | once using the discriminator, and pick Dog. 227 | Then in Dog, we will make an instance of the 228 | Animal class but this time we won't travel 229 | through its discriminator because we passed in 230 | _visited_composed_classes = (Animal,) 231 | """ 232 | # required up here when default value is not given 233 | _path_to_item = kwargs.pop("_path_to_item", ()) 234 | 235 | self = super(OpenApiModel, cls).__new__(cls) 236 | 237 | if "value" in kwargs: 238 | value = kwargs.pop("value") 239 | elif args: 240 | args = list(args) 241 | value = args.pop(0) 242 | else: 243 | raise ApiTypeError( 244 | "value is required, but not passed in args or kwargs and doesn't have default", 245 | path_to_item=_path_to_item, 246 | valid_classes=(self.__class__,), 247 | ) 248 | 249 | _check_type = kwargs.pop("_check_type", True) 250 | _spec_property_naming = kwargs.pop("_spec_property_naming", False) 251 | _configuration = kwargs.pop("_configuration", None) 252 | _visited_composed_classes = kwargs.pop("_visited_composed_classes", ()) 253 | 254 | if args: 255 | raise ApiTypeError( 256 | "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." 257 | % ( 258 | args, 259 | self.__class__.__name__, 260 | ), 261 | path_to_item=_path_to_item, 262 | valid_classes=(self.__class__,), 263 | ) 264 | 265 | self._data_store = {} 266 | self._check_type = _check_type 267 | self._spec_property_naming = _spec_property_naming 268 | self._path_to_item = _path_to_item 269 | self._configuration = _configuration 270 | self._visited_composed_classes = _visited_composed_classes + (self.__class__,) 271 | self.value = value 272 | if kwargs: 273 | raise ApiTypeError( 274 | "Invalid named arguments=%s passed to %s. Remove those invalid named arguments." 275 | % ( 276 | kwargs, 277 | self.__class__.__name__, 278 | ), 279 | path_to_item=_path_to_item, 280 | valid_classes=(self.__class__,), 281 | ) 282 | 283 | return self 284 | -------------------------------------------------------------------------------- /python-client/deutschland/risikogebiete/model_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Robert Koch Institut: Corona Risikogebiete API 3 | 4 | Aktuelle Corona Risikogebietsinformationen als API # noqa: E501 5 | 6 | The version of the OpenAPI document: 1.0.0 7 | Contact: kontakt@bund.dev 8 | Generated by: https://openapi-generator.tech 9 | """ 10 | 11 | 12 | import inspect 13 | import io 14 | import os 15 | import pprint 16 | import re 17 | import tempfile 18 | from copy import deepcopy 19 | from datetime import date, datetime # noqa: F401 20 | 21 | from dateutil.parser import parse 22 | from deutschland.risikogebiete.exceptions import ( 23 | ApiAttributeError, 24 | ApiKeyError, 25 | ApiTypeError, 26 | ApiValueError, 27 | ) 28 | 29 | none_type = type(None) 30 | file_type = io.IOBase 31 | 32 | 33 | def convert_js_args_to_python_args(fn): 34 | from functools import wraps 35 | 36 | @wraps(fn) 37 | def wrapped_init(_self, *args, **kwargs): 38 | """ 39 | An attribute named `self` received from the api will conflicts with the reserved `self` 40 | parameter of a class method. During generation, `self` attributes are mapped 41 | to `_self` in models. Here, we name `_self` instead of `self` to avoid conflicts. 42 | """ 43 | spec_property_naming = kwargs.get("_spec_property_naming", False) 44 | if spec_property_naming: 45 | kwargs = change_keys_js_to_python( 46 | kwargs, _self if isinstance(_self, type) else _self.__class__ 47 | ) 48 | return fn(_self, *args, **kwargs) 49 | 50 | return wrapped_init 51 | 52 | 53 | class cached_property(object): 54 | # this caches the result of the function call for fn with no inputs 55 | # use this as a decorator on function methods that you want converted 56 | # into cached properties 57 | result_key = "_results" 58 | 59 | def __init__(self, fn): 60 | self._fn = fn 61 | 62 | def __get__(self, instance, cls=None): 63 | if self.result_key in vars(self): 64 | return vars(self)[self.result_key] 65 | else: 66 | result = self._fn() 67 | setattr(self, self.result_key, result) 68 | return result 69 | 70 | 71 | PRIMITIVE_TYPES = (list, float, int, bool, datetime, date, str, file_type) 72 | 73 | 74 | def allows_single_value_input(cls): 75 | """ 76 | This function returns True if the input composed schema model or any 77 | descendant model allows a value only input 78 | This is true for cases where oneOf contains items like: 79 | oneOf: 80 | - float 81 | - NumberWithValidation 82 | - StringEnum 83 | - ArrayModel 84 | - null 85 | TODO: lru_cache this 86 | """ 87 | if issubclass(cls, ModelSimple) or cls in PRIMITIVE_TYPES: 88 | return True 89 | elif issubclass(cls, ModelComposed): 90 | if not cls._composed_schemas["oneOf"]: 91 | return False 92 | return any(allows_single_value_input(c) for c in cls._composed_schemas["oneOf"]) 93 | return False 94 | 95 | 96 | def composed_model_input_classes(cls): 97 | """ 98 | This function returns a list of the possible models that can be accepted as 99 | inputs. 100 | TODO: lru_cache this 101 | """ 102 | if issubclass(cls, ModelSimple) or cls in PRIMITIVE_TYPES: 103 | return [cls] 104 | elif issubclass(cls, ModelNormal): 105 | if cls.discriminator is None: 106 | return [cls] 107 | else: 108 | return get_discriminated_classes(cls) 109 | elif issubclass(cls, ModelComposed): 110 | if not cls._composed_schemas["oneOf"]: 111 | return [] 112 | if cls.discriminator is None: 113 | input_classes = [] 114 | for c in cls._composed_schemas["oneOf"]: 115 | input_classes.extend(composed_model_input_classes(c)) 116 | return input_classes 117 | else: 118 | return get_discriminated_classes(cls) 119 | return [] 120 | 121 | 122 | class OpenApiModel(object): 123 | """The base class for all OpenAPIModels""" 124 | 125 | def set_attribute(self, name, value): 126 | # this is only used to set properties on self 127 | 128 | path_to_item = [] 129 | if self._path_to_item: 130 | path_to_item.extend(self._path_to_item) 131 | path_to_item.append(name) 132 | 133 | if name in self.openapi_types: 134 | required_types_mixed = self.openapi_types[name] 135 | elif self.additional_properties_type is None: 136 | raise ApiAttributeError( 137 | "{0} has no attribute '{1}'".format(type(self).__name__, name), 138 | path_to_item, 139 | ) 140 | elif self.additional_properties_type is not None: 141 | required_types_mixed = self.additional_properties_type 142 | 143 | if get_simple_class(name) != str: 144 | error_msg = type_error_message( 145 | var_name=name, var_value=name, valid_classes=(str,), key_type=True 146 | ) 147 | raise ApiTypeError( 148 | error_msg, 149 | path_to_item=path_to_item, 150 | valid_classes=(str,), 151 | key_type=True, 152 | ) 153 | 154 | if self._check_type: 155 | value = validate_and_convert_types( 156 | value, 157 | required_types_mixed, 158 | path_to_item, 159 | self._spec_property_naming, 160 | self._check_type, 161 | configuration=self._configuration, 162 | ) 163 | if (name,) in self.allowed_values: 164 | check_allowed_values(self.allowed_values, (name,), value) 165 | if (name,) in self.validations: 166 | check_validations(self.validations, (name,), value, self._configuration) 167 | self.__dict__["_data_store"][name] = value 168 | 169 | def __repr__(self): 170 | """For `print` and `pprint`""" 171 | return self.to_str() 172 | 173 | def __ne__(self, other): 174 | """Returns true if both objects are not equal""" 175 | return not self == other 176 | 177 | def __setattr__(self, attr, value): 178 | """set the value of an attribute using dot notation: `instance.attr = val`""" 179 | self[attr] = value 180 | 181 | def __getattr__(self, attr): 182 | """get the value of an attribute using dot notation: `instance.attr`""" 183 | return self.__getitem__(attr) 184 | 185 | def __copy__(self): 186 | cls = self.__class__ 187 | if self.get("_spec_property_naming", False): 188 | return cls._new_from_openapi_data(**self.__dict__) 189 | else: 190 | return new_cls.__new__(cls, **self.__dict__) 191 | 192 | def __deepcopy__(self, memo): 193 | cls = self.__class__ 194 | 195 | if self.get("_spec_property_naming", False): 196 | new_inst = cls._new_from_openapi_data() 197 | else: 198 | new_inst = cls.__new__(cls) 199 | 200 | for k, v in self.__dict__.items(): 201 | setattr(new_inst, k, deepcopy(v, memo)) 202 | return new_inst 203 | 204 | def __new__(cls, *args, **kwargs): 205 | # this function uses the discriminator to 206 | # pick a new schema/class to instantiate because a discriminator 207 | # propertyName value was passed in 208 | 209 | if len(args) == 1: 210 | arg = args[0] 211 | if arg is None and is_type_nullable(cls): 212 | # The input data is the 'null' value and the type is nullable. 213 | return None 214 | 215 | if issubclass(cls, ModelComposed) and allows_single_value_input(cls): 216 | model_kwargs = {} 217 | oneof_instance = get_oneof_instance( 218 | cls, model_kwargs, kwargs, model_arg=arg 219 | ) 220 | return oneof_instance 221 | 222 | visited_composed_classes = kwargs.get("_visited_composed_classes", ()) 223 | if cls.discriminator is None or cls in visited_composed_classes: 224 | # Use case 1: this openapi schema (cls) does not have a discriminator 225 | # Use case 2: we have already visited this class before and are sure that we 226 | # want to instantiate it this time. We have visited this class deserializing 227 | # a payload with a discriminator. During that process we traveled through 228 | # this class but did not make an instance of it. Now we are making an 229 | # instance of a composed class which contains cls in it, so this time make an instance of cls. 230 | # 231 | # Here's an example of use case 2: If Animal has a discriminator 232 | # petType and we pass in "Dog", and the class Dog 233 | # allOf includes Animal, we move through Animal 234 | # once using the discriminator, and pick Dog. 235 | # Then in the composed schema dog Dog, we will make an instance of the 236 | # Animal class (because Dal has allOf: Animal) but this time we won't travel 237 | # through Animal's discriminator because we passed in 238 | # _visited_composed_classes = (Animal,) 239 | 240 | return super(OpenApiModel, cls).__new__(cls) 241 | 242 | # Get the name and value of the discriminator property. 243 | # The discriminator name is obtained from the discriminator meta-data 244 | # and the discriminator value is obtained from the input data. 245 | discr_propertyname_py = list(cls.discriminator.keys())[0] 246 | discr_propertyname_js = cls.attribute_map[discr_propertyname_py] 247 | if discr_propertyname_js in kwargs: 248 | discr_value = kwargs[discr_propertyname_js] 249 | elif discr_propertyname_py in kwargs: 250 | discr_value = kwargs[discr_propertyname_py] 251 | else: 252 | # The input data does not contain the discriminator property. 253 | path_to_item = kwargs.get("_path_to_item", ()) 254 | raise ApiValueError( 255 | "Cannot deserialize input data due to missing discriminator. " 256 | "The discriminator property '%s' is missing at path: %s" 257 | % (discr_propertyname_js, path_to_item) 258 | ) 259 | 260 | # Implementation note: the last argument to get_discriminator_class 261 | # is a list of visited classes. get_discriminator_class may recursively 262 | # call itself and update the list of visited classes, and the initial 263 | # value must be an empty list. Hence not using 'visited_composed_classes' 264 | new_cls = get_discriminator_class(cls, discr_propertyname_py, discr_value, []) 265 | if new_cls is None: 266 | path_to_item = kwargs.get("_path_to_item", ()) 267 | disc_prop_value = kwargs.get( 268 | discr_propertyname_js, kwargs.get(discr_propertyname_py) 269 | ) 270 | raise ApiValueError( 271 | "Cannot deserialize input data due to invalid discriminator " 272 | "value. The OpenAPI document has no mapping for discriminator " 273 | "property '%s'='%s' at path: %s" 274 | % (discr_propertyname_js, disc_prop_value, path_to_item) 275 | ) 276 | 277 | if new_cls in visited_composed_classes: 278 | # if we are making an instance of a composed schema Descendent 279 | # which allOf includes Ancestor, then Ancestor contains 280 | # a discriminator that includes Descendent. 281 | # So if we make an instance of Descendent, we have to make an 282 | # instance of Ancestor to hold the allOf properties. 283 | # This code detects that use case and makes the instance of Ancestor 284 | # For example: 285 | # When making an instance of Dog, _visited_composed_classes = (Dog,) 286 | # then we make an instance of Animal to include in dog._composed_instances 287 | # so when we are here, cls is Animal 288 | # cls.discriminator != None 289 | # cls not in _visited_composed_classes 290 | # new_cls = Dog 291 | # but we know we know that we already have Dog 292 | # because it is in visited_composed_classes 293 | # so make Animal here 294 | return super(OpenApiModel, cls).__new__(cls) 295 | 296 | # Build a list containing all oneOf and anyOf descendants. 297 | oneof_anyof_classes = None 298 | if cls._composed_schemas is not None: 299 | oneof_anyof_classes = cls._composed_schemas.get( 300 | "oneOf", () 301 | ) + cls._composed_schemas.get("anyOf", ()) 302 | oneof_anyof_child = new_cls in oneof_anyof_classes 303 | kwargs["_visited_composed_classes"] = visited_composed_classes + (cls,) 304 | 305 | if cls._composed_schemas.get("allOf") and oneof_anyof_child: 306 | # Validate that we can make self because when we make the 307 | # new_cls it will not include the allOf validations in self 308 | self_inst = super(OpenApiModel, cls).__new__(cls) 309 | self_inst.__init__(*args, **kwargs) 310 | 311 | if kwargs.get("_spec_property_naming", False): 312 | # when true, implies new is from deserialization 313 | new_inst = new_cls._new_from_openapi_data(*args, **kwargs) 314 | else: 315 | new_inst = new_cls.__new__(new_cls, *args, **kwargs) 316 | new_inst.__init__(*args, **kwargs) 317 | 318 | return new_inst 319 | 320 | @classmethod 321 | @convert_js_args_to_python_args 322 | def _new_from_openapi_data(cls, *args, **kwargs): 323 | # this function uses the discriminator to 324 | # pick a new schema/class to instantiate because a discriminator 325 | # propertyName value was passed in 326 | 327 | if len(args) == 1: 328 | arg = args[0] 329 | if arg is None and is_type_nullable(cls): 330 | # The input data is the 'null' value and the type is nullable. 331 | return None 332 | 333 | if issubclass(cls, ModelComposed) and allows_single_value_input(cls): 334 | model_kwargs = {} 335 | oneof_instance = get_oneof_instance( 336 | cls, model_kwargs, kwargs, model_arg=arg 337 | ) 338 | return oneof_instance 339 | 340 | visited_composed_classes = kwargs.get("_visited_composed_classes", ()) 341 | if cls.discriminator is None or cls in visited_composed_classes: 342 | # Use case 1: this openapi schema (cls) does not have a discriminator 343 | # Use case 2: we have already visited this class before and are sure that we 344 | # want to instantiate it this time. We have visited this class deserializing 345 | # a payload with a discriminator. During that process we traveled through 346 | # this class but did not make an instance of it. Now we are making an 347 | # instance of a composed class which contains cls in it, so this time make an instance of cls. 348 | # 349 | # Here's an example of use case 2: If Animal has a discriminator 350 | # petType and we pass in "Dog", and the class Dog 351 | # allOf includes Animal, we move through Animal 352 | # once using the discriminator, and pick Dog. 353 | # Then in the composed schema dog Dog, we will make an instance of the 354 | # Animal class (because Dal has allOf: Animal) but this time we won't travel 355 | # through Animal's discriminator because we passed in 356 | # _visited_composed_classes = (Animal,) 357 | 358 | return cls._from_openapi_data(*args, **kwargs) 359 | 360 | # Get the name and value of the discriminator property. 361 | # The discriminator name is obtained from the discriminator meta-data 362 | # and the discriminator value is obtained from the input data. 363 | discr_propertyname_py = list(cls.discriminator.keys())[0] 364 | discr_propertyname_js = cls.attribute_map[discr_propertyname_py] 365 | if discr_propertyname_js in kwargs: 366 | discr_value = kwargs[discr_propertyname_js] 367 | elif discr_propertyname_py in kwargs: 368 | discr_value = kwargs[discr_propertyname_py] 369 | else: 370 | # The input data does not contain the discriminator property. 371 | path_to_item = kwargs.get("_path_to_item", ()) 372 | raise ApiValueError( 373 | "Cannot deserialize input data due to missing discriminator. " 374 | "The discriminator property '%s' is missing at path: %s" 375 | % (discr_propertyname_js, path_to_item) 376 | ) 377 | 378 | # Implementation note: the last argument to get_discriminator_class 379 | # is a list of visited classes. get_discriminator_class may recursively 380 | # call itself and update the list of visited classes, and the initial 381 | # value must be an empty list. Hence not using 'visited_composed_classes' 382 | new_cls = get_discriminator_class(cls, discr_propertyname_py, discr_value, []) 383 | if new_cls is None: 384 | path_to_item = kwargs.get("_path_to_item", ()) 385 | disc_prop_value = kwargs.get( 386 | discr_propertyname_js, kwargs.get(discr_propertyname_py) 387 | ) 388 | raise ApiValueError( 389 | "Cannot deserialize input data due to invalid discriminator " 390 | "value. The OpenAPI document has no mapping for discriminator " 391 | "property '%s'='%s' at path: %s" 392 | % (discr_propertyname_js, disc_prop_value, path_to_item) 393 | ) 394 | 395 | if new_cls in visited_composed_classes: 396 | # if we are making an instance of a composed schema Descendent 397 | # which allOf includes Ancestor, then Ancestor contains 398 | # a discriminator that includes Descendent. 399 | # So if we make an instance of Descendent, we have to make an 400 | # instance of Ancestor to hold the allOf properties. 401 | # This code detects that use case and makes the instance of Ancestor 402 | # For example: 403 | # When making an instance of Dog, _visited_composed_classes = (Dog,) 404 | # then we make an instance of Animal to include in dog._composed_instances 405 | # so when we are here, cls is Animal 406 | # cls.discriminator != None 407 | # cls not in _visited_composed_classes 408 | # new_cls = Dog 409 | # but we know we know that we already have Dog 410 | # because it is in visited_composed_classes 411 | # so make Animal here 412 | return cls._from_openapi_data(*args, **kwargs) 413 | 414 | # Build a list containing all oneOf and anyOf descendants. 415 | oneof_anyof_classes = None 416 | if cls._composed_schemas is not None: 417 | oneof_anyof_classes = cls._composed_schemas.get( 418 | "oneOf", () 419 | ) + cls._composed_schemas.get("anyOf", ()) 420 | oneof_anyof_child = new_cls in oneof_anyof_classes 421 | kwargs["_visited_composed_classes"] = visited_composed_classes + (cls,) 422 | 423 | if cls._composed_schemas.get("allOf") and oneof_anyof_child: 424 | # Validate that we can make self because when we make the 425 | # new_cls it will not include the allOf validations in self 426 | self_inst = cls._from_openapi_data(*args, **kwargs) 427 | 428 | new_inst = new_cls._new_from_openapi_data(*args, **kwargs) 429 | return new_inst 430 | 431 | 432 | class ModelSimple(OpenApiModel): 433 | """the parent class of models whose type != object in their 434 | swagger/openapi""" 435 | 436 | def __setitem__(self, name, value): 437 | """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" 438 | if name in self.required_properties: 439 | self.__dict__[name] = value 440 | return 441 | 442 | self.set_attribute(name, value) 443 | 444 | def get(self, name, default=None): 445 | """returns the value of an attribute or some default value if the attribute was not set""" 446 | if name in self.required_properties: 447 | return self.__dict__[name] 448 | 449 | return self.__dict__["_data_store"].get(name, default) 450 | 451 | def __getitem__(self, name): 452 | """get the value of an attribute using square-bracket notation: `instance[attr]`""" 453 | if name in self: 454 | return self.get(name) 455 | 456 | raise ApiAttributeError( 457 | "{0} has no attribute '{1}'".format(type(self).__name__, name), 458 | [e for e in [self._path_to_item, name] if e], 459 | ) 460 | 461 | def __contains__(self, name): 462 | """used by `in` operator to check if an attribute value was set in an instance: `'attr' in instance`""" 463 | if name in self.required_properties: 464 | return name in self.__dict__ 465 | 466 | return name in self.__dict__["_data_store"] 467 | 468 | def to_str(self): 469 | """Returns the string representation of the model""" 470 | return str(self.value) 471 | 472 | def __eq__(self, other): 473 | """Returns true if both objects are equal""" 474 | if not isinstance(other, self.__class__): 475 | return False 476 | 477 | this_val = self._data_store["value"] 478 | that_val = other._data_store["value"] 479 | types = set() 480 | types.add(this_val.__class__) 481 | types.add(that_val.__class__) 482 | vals_equal = this_val == that_val 483 | return vals_equal 484 | 485 | 486 | class ModelNormal(OpenApiModel): 487 | """the parent class of models whose type == object in their 488 | swagger/openapi""" 489 | 490 | def __setitem__(self, name, value): 491 | """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" 492 | if name in self.required_properties: 493 | self.__dict__[name] = value 494 | return 495 | 496 | self.set_attribute(name, value) 497 | 498 | def get(self, name, default=None): 499 | """returns the value of an attribute or some default value if the attribute was not set""" 500 | if name in self.required_properties: 501 | return self.__dict__[name] 502 | 503 | return self.__dict__["_data_store"].get(name, default) 504 | 505 | def __getitem__(self, name): 506 | """get the value of an attribute using square-bracket notation: `instance[attr]`""" 507 | if name in self: 508 | return self.get(name) 509 | 510 | raise ApiAttributeError( 511 | "{0} has no attribute '{1}'".format(type(self).__name__, name), 512 | [e for e in [self._path_to_item, name] if e], 513 | ) 514 | 515 | def __contains__(self, name): 516 | """used by `in` operator to check if an attribute value was set in an instance: `'attr' in instance`""" 517 | if name in self.required_properties: 518 | return name in self.__dict__ 519 | 520 | return name in self.__dict__["_data_store"] 521 | 522 | def to_dict(self): 523 | """Returns the model properties as a dict""" 524 | return model_to_dict(self, serialize=False) 525 | 526 | def to_str(self): 527 | """Returns the string representation of the model""" 528 | return pprint.pformat(self.to_dict()) 529 | 530 | def __eq__(self, other): 531 | """Returns true if both objects are equal""" 532 | if not isinstance(other, self.__class__): 533 | return False 534 | 535 | if not set(self._data_store.keys()) == set(other._data_store.keys()): 536 | return False 537 | for _var_name, this_val in self._data_store.items(): 538 | that_val = other._data_store[_var_name] 539 | types = set() 540 | types.add(this_val.__class__) 541 | types.add(that_val.__class__) 542 | vals_equal = this_val == that_val 543 | if not vals_equal: 544 | return False 545 | return True 546 | 547 | 548 | class ModelComposed(OpenApiModel): 549 | """the parent class of models whose type == object in their 550 | swagger/openapi and have oneOf/allOf/anyOf 551 | 552 | When one sets a property we use var_name_to_model_instances to store the value in 553 | the correct class instances + run any type checking + validation code. 554 | When one gets a property we use var_name_to_model_instances to get the value 555 | from the correct class instances. 556 | This allows multiple composed schemas to contain the same property with additive 557 | constraints on the value. 558 | 559 | _composed_schemas (dict) stores the anyOf/allOf/oneOf classes 560 | key (str): allOf/oneOf/anyOf 561 | value (list): the classes in the XOf definition. 562 | Note: none_type can be included when the openapi document version >= 3.1.0 563 | _composed_instances (list): stores a list of instances of the composed schemas 564 | defined in _composed_schemas. When properties are accessed in the self instance, 565 | they are returned from the self._data_store or the data stores in the instances 566 | in self._composed_schemas 567 | _var_name_to_model_instances (dict): maps between a variable name on self and 568 | the composed instances (self included) which contain that data 569 | key (str): property name 570 | value (list): list of class instances, self or instances in _composed_instances 571 | which contain the value that the key is referring to. 572 | """ 573 | 574 | def __setitem__(self, name, value): 575 | """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" 576 | if name in self.required_properties: 577 | self.__dict__[name] = value 578 | return 579 | 580 | """ 581 | Use cases: 582 | 1. additional_properties_type is None (additionalProperties == False in spec) 583 | Check for property presence in self.openapi_types 584 | if not present then throw an error 585 | if present set in self, set attribute 586 | always set on composed schemas 587 | 2. additional_properties_type exists 588 | set attribute on self 589 | always set on composed schemas 590 | """ 591 | if self.additional_properties_type is None: 592 | """ 593 | For an attribute to exist on a composed schema it must: 594 | - fulfill schema_requirements in the self composed schema not considering oneOf/anyOf/allOf schemas AND 595 | - fulfill schema_requirements in each oneOf/anyOf/allOf schemas 596 | 597 | schema_requirements: 598 | For an attribute to exist on a schema it must: 599 | - be present in properties at the schema OR 600 | - have additionalProperties unset (defaults additionalProperties = any type) OR 601 | - have additionalProperties set 602 | """ 603 | if name not in self.openapi_types: 604 | raise ApiAttributeError( 605 | "{0} has no attribute '{1}'".format(type(self).__name__, name), 606 | [e for e in [self._path_to_item, name] if e], 607 | ) 608 | # attribute must be set on self and composed instances 609 | self.set_attribute(name, value) 610 | for model_instance in self._composed_instances: 611 | setattr(model_instance, name, value) 612 | if name not in self._var_name_to_model_instances: 613 | # we assigned an additional property 614 | self.__dict__["_var_name_to_model_instances"][ 615 | name 616 | ] = self._composed_instances + [self] 617 | return None 618 | 619 | __unset_attribute_value__ = object() 620 | 621 | def get(self, name, default=None): 622 | """returns the value of an attribute or some default value if the attribute was not set""" 623 | if name in self.required_properties: 624 | return self.__dict__[name] 625 | 626 | # get the attribute from the correct instance 627 | model_instances = self._var_name_to_model_instances.get(name) 628 | values = [] 629 | # A composed model stores self and child (oneof/anyOf/allOf) models under 630 | # self._var_name_to_model_instances. 631 | # Any property must exist in self and all model instances 632 | # The value stored in all model instances must be the same 633 | if model_instances: 634 | for model_instance in model_instances: 635 | if name in model_instance._data_store: 636 | v = model_instance._data_store[name] 637 | if v not in values: 638 | values.append(v) 639 | len_values = len(values) 640 | if len_values == 0: 641 | return default 642 | elif len_values == 1: 643 | return values[0] 644 | elif len_values > 1: 645 | raise ApiValueError( 646 | "Values stored for property {0} in {1} differ when looking " 647 | "at self and self's composed instances. All values must be " 648 | "the same".format(name, type(self).__name__), 649 | [e for e in [self._path_to_item, name] if e], 650 | ) 651 | 652 | def __getitem__(self, name): 653 | """get the value of an attribute using square-bracket notation: `instance[attr]`""" 654 | value = self.get(name, self.__unset_attribute_value__) 655 | if value is self.__unset_attribute_value__: 656 | raise ApiAttributeError( 657 | "{0} has no attribute '{1}'".format(type(self).__name__, name), 658 | [e for e in [self._path_to_item, name] if e], 659 | ) 660 | return value 661 | 662 | def __contains__(self, name): 663 | """used by `in` operator to check if an attribute value was set in an instance: `'attr' in instance`""" 664 | 665 | if name in self.required_properties: 666 | return name in self.__dict__ 667 | 668 | model_instances = self._var_name_to_model_instances.get( 669 | name, self._additional_properties_model_instances 670 | ) 671 | 672 | if model_instances: 673 | for model_instance in model_instances: 674 | if name in model_instance._data_store: 675 | return True 676 | 677 | return False 678 | 679 | def to_dict(self): 680 | """Returns the model properties as a dict""" 681 | return model_to_dict(self, serialize=False) 682 | 683 | def to_str(self): 684 | """Returns the string representation of the model""" 685 | return pprint.pformat(self.to_dict()) 686 | 687 | def __eq__(self, other): 688 | """Returns true if both objects are equal""" 689 | if not isinstance(other, self.__class__): 690 | return False 691 | 692 | if not set(self._data_store.keys()) == set(other._data_store.keys()): 693 | return False 694 | for _var_name, this_val in self._data_store.items(): 695 | that_val = other._data_store[_var_name] 696 | types = set() 697 | types.add(this_val.__class__) 698 | types.add(that_val.__class__) 699 | vals_equal = this_val == that_val 700 | if not vals_equal: 701 | return False 702 | return True 703 | 704 | 705 | COERCION_INDEX_BY_TYPE = { 706 | ModelComposed: 0, 707 | ModelNormal: 1, 708 | ModelSimple: 2, 709 | none_type: 3, # The type of 'None'. 710 | list: 4, 711 | dict: 5, 712 | float: 6, 713 | int: 7, 714 | bool: 8, 715 | datetime: 9, 716 | date: 10, 717 | str: 11, 718 | file_type: 12, # 'file_type' is an alias for the built-in 'file' or 'io.IOBase' type. 719 | } 720 | 721 | # these are used to limit what type conversions we try to do 722 | # when we have a valid type already and we want to try converting 723 | # to another type 724 | UPCONVERSION_TYPE_PAIRS = ( 725 | (str, datetime), 726 | (str, date), 727 | ( 728 | int, 729 | float, 730 | ), # A float may be serialized as an integer, e.g. '3' is a valid serialized float. 731 | (list, ModelComposed), 732 | (dict, ModelComposed), 733 | (str, ModelComposed), 734 | (int, ModelComposed), 735 | (float, ModelComposed), 736 | (list, ModelComposed), 737 | (list, ModelNormal), 738 | (dict, ModelNormal), 739 | (str, ModelSimple), 740 | (int, ModelSimple), 741 | (float, ModelSimple), 742 | (list, ModelSimple), 743 | ) 744 | 745 | COERCIBLE_TYPE_PAIRS = { 746 | False: ( # client instantiation of a model with client data 747 | # (dict, ModelComposed), 748 | # (list, ModelComposed), 749 | # (dict, ModelNormal), 750 | # (list, ModelNormal), 751 | # (str, ModelSimple), 752 | # (int, ModelSimple), 753 | # (float, ModelSimple), 754 | # (list, ModelSimple), 755 | # (str, int), 756 | # (str, float), 757 | # (str, datetime), 758 | # (str, date), 759 | # (int, str), 760 | # (float, str), 761 | ), 762 | True: ( # server -> client data 763 | (dict, ModelComposed), 764 | (list, ModelComposed), 765 | (dict, ModelNormal), 766 | (list, ModelNormal), 767 | (str, ModelSimple), 768 | (int, ModelSimple), 769 | (float, ModelSimple), 770 | (list, ModelSimple), 771 | # (str, int), 772 | # (str, float), 773 | (str, datetime), 774 | (str, date), 775 | # (int, str), 776 | # (float, str), 777 | (str, file_type), 778 | ), 779 | } 780 | 781 | 782 | def get_simple_class(input_value): 783 | """Returns an input_value's simple class that we will use for type checking 784 | Python2: 785 | float and int will return int, where int is the python3 int backport 786 | str and unicode will return str, where str is the python3 str backport 787 | Note: float and int ARE both instances of int backport 788 | Note: str_py2 and unicode_py2 are NOT both instances of str backport 789 | 790 | Args: 791 | input_value (class/class_instance): the item for which we will return 792 | the simple class 793 | """ 794 | if isinstance(input_value, type): 795 | # input_value is a class 796 | return input_value 797 | elif isinstance(input_value, tuple): 798 | return tuple 799 | elif isinstance(input_value, list): 800 | return list 801 | elif isinstance(input_value, dict): 802 | return dict 803 | elif isinstance(input_value, none_type): 804 | return none_type 805 | elif isinstance(input_value, file_type): 806 | return file_type 807 | elif isinstance(input_value, bool): 808 | # this must be higher than the int check because 809 | # isinstance(True, int) == True 810 | return bool 811 | elif isinstance(input_value, int): 812 | return int 813 | elif isinstance(input_value, datetime): 814 | # this must be higher than the date check because 815 | # isinstance(datetime_instance, date) == True 816 | return datetime 817 | elif isinstance(input_value, date): 818 | return date 819 | elif isinstance(input_value, str): 820 | return str 821 | return type(input_value) 822 | 823 | 824 | def check_allowed_values(allowed_values, input_variable_path, input_values): 825 | """Raises an exception if the input_values are not allowed 826 | 827 | Args: 828 | allowed_values (dict): the allowed_values dict 829 | input_variable_path (tuple): the path to the input variable 830 | input_values (list/str/int/float/date/datetime): the values that we 831 | are checking to see if they are in allowed_values 832 | """ 833 | these_allowed_values = list(allowed_values[input_variable_path].values()) 834 | if isinstance(input_values, list) and not set(input_values).issubset( 835 | set(these_allowed_values) 836 | ): 837 | invalid_values = ( 838 | ", ".join(map(str, set(input_values) - set(these_allowed_values))), 839 | ) 840 | raise ApiValueError( 841 | "Invalid values for `%s` [%s], must be a subset of [%s]" 842 | % ( 843 | input_variable_path[0], 844 | invalid_values, 845 | ", ".join(map(str, these_allowed_values)), 846 | ) 847 | ) 848 | elif isinstance(input_values, dict) and not set(input_values.keys()).issubset( 849 | set(these_allowed_values) 850 | ): 851 | invalid_values = ", ".join( 852 | map(str, set(input_values.keys()) - set(these_allowed_values)) 853 | ) 854 | raise ApiValueError( 855 | "Invalid keys in `%s` [%s], must be a subset of [%s]" 856 | % ( 857 | input_variable_path[0], 858 | invalid_values, 859 | ", ".join(map(str, these_allowed_values)), 860 | ) 861 | ) 862 | elif ( 863 | not isinstance(input_values, (list, dict)) 864 | and input_values not in these_allowed_values 865 | ): 866 | raise ApiValueError( 867 | "Invalid value for `%s` (%s), must be one of %s" 868 | % (input_variable_path[0], input_values, these_allowed_values) 869 | ) 870 | 871 | 872 | def is_json_validation_enabled(schema_keyword, configuration=None): 873 | """Returns true if JSON schema validation is enabled for the specified 874 | validation keyword. This can be used to skip JSON schema structural validation 875 | as requested in the configuration. 876 | 877 | Args: 878 | schema_keyword (string): the name of a JSON schema validation keyword. 879 | configuration (Configuration): the configuration class. 880 | """ 881 | 882 | return ( 883 | configuration is None 884 | or not hasattr(configuration, "_disabled_client_side_validations") 885 | or schema_keyword not in configuration._disabled_client_side_validations 886 | ) 887 | 888 | 889 | def check_validations( 890 | validations, input_variable_path, input_values, configuration=None 891 | ): 892 | """Raises an exception if the input_values are invalid 893 | 894 | Args: 895 | validations (dict): the validation dictionary. 896 | input_variable_path (tuple): the path to the input variable. 897 | input_values (list/str/int/float/date/datetime): the values that we 898 | are checking. 899 | configuration (Configuration): the configuration class. 900 | """ 901 | 902 | if input_values is None: 903 | return 904 | 905 | current_validations = validations[input_variable_path] 906 | if ( 907 | is_json_validation_enabled("multipleOf", configuration) 908 | and "multiple_of" in current_validations 909 | and isinstance(input_values, (int, float)) 910 | and not (float(input_values) / current_validations["multiple_of"]).is_integer() 911 | ): 912 | # Note 'multipleOf' will be as good as the floating point arithmetic. 913 | raise ApiValueError( 914 | "Invalid value for `%s`, value must be a multiple of " 915 | "`%s`" % (input_variable_path[0], current_validations["multiple_of"]) 916 | ) 917 | 918 | if ( 919 | is_json_validation_enabled("maxLength", configuration) 920 | and "max_length" in current_validations 921 | and len(input_values) > current_validations["max_length"] 922 | ): 923 | raise ApiValueError( 924 | "Invalid value for `%s`, length must be less than or equal to " 925 | "`%s`" % (input_variable_path[0], current_validations["max_length"]) 926 | ) 927 | 928 | if ( 929 | is_json_validation_enabled("minLength", configuration) 930 | and "min_length" in current_validations 931 | and len(input_values) < current_validations["min_length"] 932 | ): 933 | raise ApiValueError( 934 | "Invalid value for `%s`, length must be greater than or equal to " 935 | "`%s`" % (input_variable_path[0], current_validations["min_length"]) 936 | ) 937 | 938 | if ( 939 | is_json_validation_enabled("maxItems", configuration) 940 | and "max_items" in current_validations 941 | and len(input_values) > current_validations["max_items"] 942 | ): 943 | raise ApiValueError( 944 | "Invalid value for `%s`, number of items must be less than or " 945 | "equal to `%s`" % (input_variable_path[0], current_validations["max_items"]) 946 | ) 947 | 948 | if ( 949 | is_json_validation_enabled("minItems", configuration) 950 | and "min_items" in current_validations 951 | and len(input_values) < current_validations["min_items"] 952 | ): 953 | raise ValueError( 954 | "Invalid value for `%s`, number of items must be greater than or " 955 | "equal to `%s`" % (input_variable_path[0], current_validations["min_items"]) 956 | ) 957 | 958 | items = ( 959 | "exclusive_maximum", 960 | "inclusive_maximum", 961 | "exclusive_minimum", 962 | "inclusive_minimum", 963 | ) 964 | if any(item in current_validations for item in items): 965 | if isinstance(input_values, list): 966 | max_val = max(input_values) 967 | min_val = min(input_values) 968 | elif isinstance(input_values, dict): 969 | max_val = max(input_values.values()) 970 | min_val = min(input_values.values()) 971 | else: 972 | max_val = input_values 973 | min_val = input_values 974 | 975 | if ( 976 | is_json_validation_enabled("exclusiveMaximum", configuration) 977 | and "exclusive_maximum" in current_validations 978 | and max_val >= current_validations["exclusive_maximum"] 979 | ): 980 | raise ApiValueError( 981 | "Invalid value for `%s`, must be a value less than `%s`" 982 | % (input_variable_path[0], current_validations["exclusive_maximum"]) 983 | ) 984 | 985 | if ( 986 | is_json_validation_enabled("maximum", configuration) 987 | and "inclusive_maximum" in current_validations 988 | and max_val > current_validations["inclusive_maximum"] 989 | ): 990 | raise ApiValueError( 991 | "Invalid value for `%s`, must be a value less than or equal to " 992 | "`%s`" % (input_variable_path[0], current_validations["inclusive_maximum"]) 993 | ) 994 | 995 | if ( 996 | is_json_validation_enabled("exclusiveMinimum", configuration) 997 | and "exclusive_minimum" in current_validations 998 | and min_val <= current_validations["exclusive_minimum"] 999 | ): 1000 | raise ApiValueError( 1001 | "Invalid value for `%s`, must be a value greater than `%s`" 1002 | % (input_variable_path[0], current_validations["exclusive_maximum"]) 1003 | ) 1004 | 1005 | if ( 1006 | is_json_validation_enabled("minimum", configuration) 1007 | and "inclusive_minimum" in current_validations 1008 | and min_val < current_validations["inclusive_minimum"] 1009 | ): 1010 | raise ApiValueError( 1011 | "Invalid value for `%s`, must be a value greater than or equal " 1012 | "to `%s`" 1013 | % (input_variable_path[0], current_validations["inclusive_minimum"]) 1014 | ) 1015 | flags = current_validations.get("regex", {}).get("flags", 0) 1016 | if ( 1017 | is_json_validation_enabled("pattern", configuration) 1018 | and "regex" in current_validations 1019 | and not re.search( 1020 | current_validations["regex"]["pattern"], input_values, flags=flags 1021 | ) 1022 | ): 1023 | err_msg = r"Invalid value for `%s`, must match regular expression `%s`" % ( 1024 | input_variable_path[0], 1025 | current_validations["regex"]["pattern"], 1026 | ) 1027 | if flags != 0: 1028 | # Don't print the regex flags if the flags are not 1029 | # specified in the OAS document. 1030 | err_msg = r"%s with flags=`%s`" % (err_msg, flags) 1031 | raise ApiValueError(err_msg) 1032 | 1033 | 1034 | def order_response_types(required_types): 1035 | """Returns the required types sorted in coercion order 1036 | 1037 | Args: 1038 | required_types (list/tuple): collection of classes or instance of 1039 | list or dict with class information inside it. 1040 | 1041 | Returns: 1042 | (list): coercion order sorted collection of classes or instance 1043 | of list or dict with class information inside it. 1044 | """ 1045 | 1046 | def index_getter(class_or_instance): 1047 | if isinstance(class_or_instance, list): 1048 | return COERCION_INDEX_BY_TYPE[list] 1049 | elif isinstance(class_or_instance, dict): 1050 | return COERCION_INDEX_BY_TYPE[dict] 1051 | elif inspect.isclass(class_or_instance) and issubclass( 1052 | class_or_instance, ModelComposed 1053 | ): 1054 | return COERCION_INDEX_BY_TYPE[ModelComposed] 1055 | elif inspect.isclass(class_or_instance) and issubclass( 1056 | class_or_instance, ModelNormal 1057 | ): 1058 | return COERCION_INDEX_BY_TYPE[ModelNormal] 1059 | elif inspect.isclass(class_or_instance) and issubclass( 1060 | class_or_instance, ModelSimple 1061 | ): 1062 | return COERCION_INDEX_BY_TYPE[ModelSimple] 1063 | elif class_or_instance in COERCION_INDEX_BY_TYPE: 1064 | return COERCION_INDEX_BY_TYPE[class_or_instance] 1065 | raise ApiValueError("Unsupported type: %s" % class_or_instance) 1066 | 1067 | sorted_types = sorted( 1068 | required_types, key=lambda class_or_instance: index_getter(class_or_instance) 1069 | ) 1070 | return sorted_types 1071 | 1072 | 1073 | def remove_uncoercible( 1074 | required_types_classes, current_item, spec_property_naming, must_convert=True 1075 | ): 1076 | """Only keeps the type conversions that are possible 1077 | 1078 | Args: 1079 | required_types_classes (tuple): tuple of classes that are required 1080 | these should be ordered by COERCION_INDEX_BY_TYPE 1081 | spec_property_naming (bool): True if the variable names in the input 1082 | data are serialized names as specified in the OpenAPI document. 1083 | False if the variables names in the input data are python 1084 | variable names in PEP-8 snake case. 1085 | current_item (any): the current item (input data) to be converted 1086 | 1087 | Keyword Args: 1088 | must_convert (bool): if True the item to convert is of the wrong 1089 | type and we want a big list of coercibles 1090 | if False, we want a limited list of coercibles 1091 | 1092 | Returns: 1093 | (list): the remaining coercible required types, classes only 1094 | """ 1095 | current_type_simple = get_simple_class(current_item) 1096 | 1097 | results_classes = [] 1098 | for required_type_class in required_types_classes: 1099 | # convert our models to OpenApiModel 1100 | required_type_class_simplified = required_type_class 1101 | if isinstance(required_type_class_simplified, type): 1102 | if issubclass(required_type_class_simplified, ModelComposed): 1103 | required_type_class_simplified = ModelComposed 1104 | elif issubclass(required_type_class_simplified, ModelNormal): 1105 | required_type_class_simplified = ModelNormal 1106 | elif issubclass(required_type_class_simplified, ModelSimple): 1107 | required_type_class_simplified = ModelSimple 1108 | 1109 | if required_type_class_simplified == current_type_simple: 1110 | # don't consider converting to one's own class 1111 | continue 1112 | 1113 | class_pair = (current_type_simple, required_type_class_simplified) 1114 | if must_convert and class_pair in COERCIBLE_TYPE_PAIRS[spec_property_naming]: 1115 | results_classes.append(required_type_class) 1116 | elif class_pair in UPCONVERSION_TYPE_PAIRS: 1117 | results_classes.append(required_type_class) 1118 | return results_classes 1119 | 1120 | 1121 | def get_discriminated_classes(cls): 1122 | """ 1123 | Returns all the classes that a discriminator converts to 1124 | TODO: lru_cache this 1125 | """ 1126 | possible_classes = [] 1127 | key = list(cls.discriminator.keys())[0] 1128 | if is_type_nullable(cls): 1129 | possible_classes.append(cls) 1130 | for discr_cls in cls.discriminator[key].values(): 1131 | if hasattr(discr_cls, "discriminator") and discr_cls.discriminator is not None: 1132 | possible_classes.extend(get_discriminated_classes(discr_cls)) 1133 | else: 1134 | possible_classes.append(discr_cls) 1135 | return possible_classes 1136 | 1137 | 1138 | def get_possible_classes(cls, from_server_context): 1139 | # TODO: lru_cache this 1140 | possible_classes = [cls] 1141 | if from_server_context: 1142 | return possible_classes 1143 | if hasattr(cls, "discriminator") and cls.discriminator is not None: 1144 | possible_classes = [] 1145 | possible_classes.extend(get_discriminated_classes(cls)) 1146 | elif issubclass(cls, ModelComposed): 1147 | possible_classes.extend(composed_model_input_classes(cls)) 1148 | return possible_classes 1149 | 1150 | 1151 | def get_required_type_classes(required_types_mixed, spec_property_naming): 1152 | """Converts the tuple required_types into a tuple and a dict described 1153 | below 1154 | 1155 | Args: 1156 | required_types_mixed (tuple/list): will contain either classes or 1157 | instance of list or dict 1158 | spec_property_naming (bool): if True these values came from the 1159 | server, and we use the data types in our endpoints. 1160 | If False, we are client side and we need to include 1161 | oneOf and discriminator classes inside the data types in our endpoints 1162 | 1163 | Returns: 1164 | (valid_classes, dict_valid_class_to_child_types_mixed): 1165 | valid_classes (tuple): the valid classes that the current item 1166 | should be 1167 | dict_valid_class_to_child_types_mixed (dict): 1168 | valid_class (class): this is the key 1169 | child_types_mixed (list/dict/tuple): describes the valid child 1170 | types 1171 | """ 1172 | valid_classes = [] 1173 | child_req_types_by_current_type = {} 1174 | for required_type in required_types_mixed: 1175 | if isinstance(required_type, list): 1176 | valid_classes.append(list) 1177 | child_req_types_by_current_type[list] = required_type 1178 | elif isinstance(required_type, tuple): 1179 | valid_classes.append(tuple) 1180 | child_req_types_by_current_type[tuple] = required_type 1181 | elif isinstance(required_type, dict): 1182 | valid_classes.append(dict) 1183 | child_req_types_by_current_type[dict] = required_type[str] 1184 | else: 1185 | valid_classes.extend( 1186 | get_possible_classes(required_type, spec_property_naming) 1187 | ) 1188 | return tuple(valid_classes), child_req_types_by_current_type 1189 | 1190 | 1191 | def change_keys_js_to_python(input_dict, model_class): 1192 | """ 1193 | Converts from javascript_key keys in the input_dict to python_keys in 1194 | the output dict using the mapping in model_class. 1195 | If the input_dict contains a key which does not declared in the model_class, 1196 | the key is added to the output dict as is. The assumption is the model_class 1197 | may have undeclared properties (additionalProperties attribute in the OAS 1198 | document). 1199 | """ 1200 | 1201 | if getattr(model_class, "attribute_map", None) is None: 1202 | return input_dict 1203 | output_dict = {} 1204 | reversed_attr_map = {value: key for key, value in model_class.attribute_map.items()} 1205 | for javascript_key, value in input_dict.items(): 1206 | python_key = reversed_attr_map.get(javascript_key) 1207 | if python_key is None: 1208 | # if the key is unknown, it is in error or it is an 1209 | # additionalProperties variable 1210 | python_key = javascript_key 1211 | output_dict[python_key] = value 1212 | return output_dict 1213 | 1214 | 1215 | def get_type_error(var_value, path_to_item, valid_classes, key_type=False): 1216 | error_msg = type_error_message( 1217 | var_name=path_to_item[-1], 1218 | var_value=var_value, 1219 | valid_classes=valid_classes, 1220 | key_type=key_type, 1221 | ) 1222 | return ApiTypeError( 1223 | error_msg, 1224 | path_to_item=path_to_item, 1225 | valid_classes=valid_classes, 1226 | key_type=key_type, 1227 | ) 1228 | 1229 | 1230 | def deserialize_primitive(data, klass, path_to_item): 1231 | """Deserializes string to primitive type. 1232 | 1233 | :param data: str/int/float 1234 | :param klass: str/class the class to convert to 1235 | 1236 | :return: int, float, str, bool, date, datetime 1237 | """ 1238 | additional_message = "" 1239 | try: 1240 | if klass in {datetime, date}: 1241 | additional_message = ( 1242 | "If you need your parameter to have a fallback " 1243 | "string value, please set its type as `type: {}` in your " 1244 | "spec. That allows the value to be any type. " 1245 | ) 1246 | if klass == datetime: 1247 | if len(data) < 8: 1248 | raise ValueError("This is not a datetime") 1249 | # The string should be in iso8601 datetime format. 1250 | parsed_datetime = parse(data) 1251 | date_only = ( 1252 | parsed_datetime.hour == 0 1253 | and parsed_datetime.minute == 0 1254 | and parsed_datetime.second == 0 1255 | and parsed_datetime.tzinfo is None 1256 | and 8 <= len(data) <= 10 1257 | ) 1258 | if date_only: 1259 | raise ValueError("This is a date, not a datetime") 1260 | return parsed_datetime 1261 | elif klass == date: 1262 | if len(data) < 8: 1263 | raise ValueError("This is not a date") 1264 | return parse(data).date() 1265 | else: 1266 | converted_value = klass(data) 1267 | if isinstance(data, str) and klass == float: 1268 | if str(converted_value) != data: 1269 | # '7' -> 7.0 -> '7.0' != '7' 1270 | raise ValueError("This is not a float") 1271 | return converted_value 1272 | except (OverflowError, ValueError) as ex: 1273 | # parse can raise OverflowError 1274 | raise ApiValueError( 1275 | "{0}Failed to parse {1} as {2}".format( 1276 | additional_message, repr(data), klass.__name__ 1277 | ), 1278 | path_to_item=path_to_item, 1279 | ) from ex 1280 | 1281 | 1282 | def get_discriminator_class(model_class, discr_name, discr_value, cls_visited): 1283 | """Returns the child class specified by the discriminator. 1284 | 1285 | Args: 1286 | model_class (OpenApiModel): the model class. 1287 | discr_name (string): the name of the discriminator property. 1288 | discr_value (any): the discriminator value. 1289 | cls_visited (list): list of model classes that have been visited. 1290 | Used to determine the discriminator class without 1291 | visiting circular references indefinitely. 1292 | 1293 | Returns: 1294 | used_model_class (class/None): the chosen child class that will be used 1295 | to deserialize the data, for example dog.Dog. 1296 | If a class is not found, None is returned. 1297 | """ 1298 | 1299 | if model_class in cls_visited: 1300 | # The class has already been visited and no suitable class was found. 1301 | return None 1302 | cls_visited.append(model_class) 1303 | used_model_class = None 1304 | if discr_name in model_class.discriminator: 1305 | class_name_to_discr_class = model_class.discriminator[discr_name] 1306 | used_model_class = class_name_to_discr_class.get(discr_value) 1307 | if used_model_class is None: 1308 | # We didn't find a discriminated class in class_name_to_discr_class. 1309 | # So look in the ancestor or descendant discriminators 1310 | # The discriminator mapping may exist in a descendant (anyOf, oneOf) 1311 | # or ancestor (allOf). 1312 | # Ancestor example: in the GrandparentAnimal -> ParentPet -> ChildCat 1313 | # hierarchy, the discriminator mappings may be defined at any level 1314 | # in the hierarchy. 1315 | # Descendant example: mammal -> whale/zebra/Pig -> BasquePig/DanishPig 1316 | # if we try to make BasquePig from mammal, we need to travel through 1317 | # the oneOf descendant discriminators to find BasquePig 1318 | descendant_classes = model_class._composed_schemas.get( 1319 | "oneOf", () 1320 | ) + model_class._composed_schemas.get("anyOf", ()) 1321 | ancestor_classes = model_class._composed_schemas.get("allOf", ()) 1322 | possible_classes = descendant_classes + ancestor_classes 1323 | for cls in possible_classes: 1324 | # Check if the schema has inherited discriminators. 1325 | if hasattr(cls, "discriminator") and cls.discriminator is not None: 1326 | used_model_class = get_discriminator_class( 1327 | cls, discr_name, discr_value, cls_visited 1328 | ) 1329 | if used_model_class is not None: 1330 | return used_model_class 1331 | return used_model_class 1332 | 1333 | 1334 | def deserialize_model( 1335 | model_data, 1336 | model_class, 1337 | path_to_item, 1338 | check_type, 1339 | configuration, 1340 | spec_property_naming, 1341 | ): 1342 | """Deserializes model_data to model instance. 1343 | 1344 | Args: 1345 | model_data (int/str/float/bool/none_type/list/dict): data to instantiate the model 1346 | model_class (OpenApiModel): the model class 1347 | path_to_item (list): path to the model in the received data 1348 | check_type (bool): whether to check the data tupe for the values in 1349 | the model 1350 | configuration (Configuration): the instance to use to convert files 1351 | spec_property_naming (bool): True if the variable names in the input 1352 | data are serialized names as specified in the OpenAPI document. 1353 | False if the variables names in the input data are python 1354 | variable names in PEP-8 snake case. 1355 | 1356 | Returns: 1357 | model instance 1358 | 1359 | Raise: 1360 | ApiTypeError 1361 | ApiValueError 1362 | ApiKeyError 1363 | """ 1364 | 1365 | kw_args = dict( 1366 | _check_type=check_type, 1367 | _path_to_item=path_to_item, 1368 | _configuration=configuration, 1369 | _spec_property_naming=spec_property_naming, 1370 | ) 1371 | 1372 | if issubclass(model_class, ModelSimple): 1373 | return model_class._new_from_openapi_data(model_data, **kw_args) 1374 | elif isinstance(model_data, list): 1375 | return model_class._new_from_openapi_data(*model_data, **kw_args) 1376 | if isinstance(model_data, dict): 1377 | kw_args.update(model_data) 1378 | return model_class._new_from_openapi_data(**kw_args) 1379 | elif isinstance(model_data, PRIMITIVE_TYPES): 1380 | return model_class._new_from_openapi_data(model_data, **kw_args) 1381 | 1382 | 1383 | def deserialize_file(response_data, configuration, content_disposition=None): 1384 | """Deserializes body to file 1385 | 1386 | Saves response body into a file in a temporary folder, 1387 | using the filename from the `Content-Disposition` header if provided. 1388 | 1389 | Args: 1390 | param response_data (str): the file data to write 1391 | configuration (Configuration): the instance to use to convert files 1392 | 1393 | Keyword Args: 1394 | content_disposition (str): the value of the Content-Disposition 1395 | header 1396 | 1397 | Returns: 1398 | (file_type): the deserialized file which is open 1399 | The user is responsible for closing and reading the file 1400 | """ 1401 | fd, path = tempfile.mkstemp(dir=configuration.temp_folder_path) 1402 | os.close(fd) 1403 | os.remove(path) 1404 | 1405 | if content_disposition: 1406 | filename = re.search( 1407 | r'filename=[\'"]?([^\'"\s]+)[\'"]?', content_disposition 1408 | ).group(1) 1409 | path = os.path.join(os.path.dirname(path), filename) 1410 | 1411 | with open(path, "wb") as f: 1412 | if isinstance(response_data, str): 1413 | # change str to bytes so we can write it 1414 | response_data = response_data.encode("utf-8") 1415 | f.write(response_data) 1416 | 1417 | f = open(path, "rb") 1418 | return f 1419 | 1420 | 1421 | def attempt_convert_item( 1422 | input_value, 1423 | valid_classes, 1424 | path_to_item, 1425 | configuration, 1426 | spec_property_naming, 1427 | key_type=False, 1428 | must_convert=False, 1429 | check_type=True, 1430 | ): 1431 | """ 1432 | Args: 1433 | input_value (any): the data to convert 1434 | valid_classes (any): the classes that are valid 1435 | path_to_item (list): the path to the item to convert 1436 | configuration (Configuration): the instance to use to convert files 1437 | spec_property_naming (bool): True if the variable names in the input 1438 | data are serialized names as specified in the OpenAPI document. 1439 | False if the variables names in the input data are python 1440 | variable names in PEP-8 snake case. 1441 | key_type (bool): if True we need to convert a key type (not supported) 1442 | must_convert (bool): if True we must convert 1443 | check_type (bool): if True we check the type or the returned data in 1444 | ModelComposed/ModelNormal/ModelSimple instances 1445 | 1446 | Returns: 1447 | instance (any) the fixed item 1448 | 1449 | Raises: 1450 | ApiTypeError 1451 | ApiValueError 1452 | ApiKeyError 1453 | """ 1454 | valid_classes_ordered = order_response_types(valid_classes) 1455 | valid_classes_coercible = remove_uncoercible( 1456 | valid_classes_ordered, input_value, spec_property_naming 1457 | ) 1458 | if not valid_classes_coercible or key_type: 1459 | # we do not handle keytype errors, json will take care 1460 | # of this for us 1461 | if configuration is None or not configuration.discard_unknown_keys: 1462 | raise get_type_error( 1463 | input_value, path_to_item, valid_classes, key_type=key_type 1464 | ) 1465 | for valid_class in valid_classes_coercible: 1466 | try: 1467 | if issubclass(valid_class, OpenApiModel): 1468 | return deserialize_model( 1469 | input_value, 1470 | valid_class, 1471 | path_to_item, 1472 | check_type, 1473 | configuration, 1474 | spec_property_naming, 1475 | ) 1476 | elif valid_class == file_type: 1477 | return deserialize_file(input_value, configuration) 1478 | return deserialize_primitive(input_value, valid_class, path_to_item) 1479 | except (ApiTypeError, ApiValueError, ApiKeyError) as conversion_exc: 1480 | if must_convert: 1481 | raise conversion_exc 1482 | # if we have conversion errors when must_convert == False 1483 | # we ignore the exception and move on to the next class 1484 | continue 1485 | # we were unable to convert, must_convert == False 1486 | return input_value 1487 | 1488 | 1489 | def is_type_nullable(input_type): 1490 | """ 1491 | Returns true if None is an allowed value for the specified input_type. 1492 | 1493 | A type is nullable if at least one of the following conditions is true: 1494 | 1. The OAS 'nullable' attribute has been specified, 1495 | 1. The type is the 'null' type, 1496 | 1. The type is a anyOf/oneOf composed schema, and a child schema is 1497 | the 'null' type. 1498 | Args: 1499 | input_type (type): the class of the input_value that we are 1500 | checking 1501 | Returns: 1502 | bool 1503 | """ 1504 | if input_type is none_type: 1505 | return True 1506 | if issubclass(input_type, OpenApiModel) and input_type._nullable: 1507 | return True 1508 | if issubclass(input_type, ModelComposed): 1509 | # If oneOf/anyOf, check if the 'null' type is one of the allowed types. 1510 | for t in input_type._composed_schemas.get("oneOf", ()): 1511 | if is_type_nullable(t): 1512 | return True 1513 | for t in input_type._composed_schemas.get("anyOf", ()): 1514 | if is_type_nullable(t): 1515 | return True 1516 | return False 1517 | 1518 | 1519 | def is_valid_type(input_class_simple, valid_classes): 1520 | """ 1521 | Args: 1522 | input_class_simple (class): the class of the input_value that we are 1523 | checking 1524 | valid_classes (tuple): the valid classes that the current item 1525 | should be 1526 | Returns: 1527 | bool 1528 | """ 1529 | if issubclass(input_class_simple, OpenApiModel) and valid_classes == ( 1530 | bool, 1531 | date, 1532 | datetime, 1533 | dict, 1534 | float, 1535 | int, 1536 | list, 1537 | str, 1538 | none_type, 1539 | ): 1540 | return True 1541 | valid_type = input_class_simple in valid_classes 1542 | if not valid_type and ( 1543 | issubclass(input_class_simple, OpenApiModel) or input_class_simple is none_type 1544 | ): 1545 | for valid_class in valid_classes: 1546 | if input_class_simple is none_type and is_type_nullable(valid_class): 1547 | # Schema is oneOf/anyOf and the 'null' type is one of the allowed types. 1548 | return True 1549 | if not ( 1550 | issubclass(valid_class, OpenApiModel) and valid_class.discriminator 1551 | ): 1552 | continue 1553 | discr_propertyname_py = list(valid_class.discriminator.keys())[0] 1554 | discriminator_classes = valid_class.discriminator[ 1555 | discr_propertyname_py 1556 | ].values() 1557 | valid_type = is_valid_type(input_class_simple, discriminator_classes) 1558 | if valid_type: 1559 | return True 1560 | return valid_type 1561 | 1562 | 1563 | def validate_and_convert_types( 1564 | input_value, 1565 | required_types_mixed, 1566 | path_to_item, 1567 | spec_property_naming, 1568 | _check_type, 1569 | configuration=None, 1570 | ): 1571 | """Raises a TypeError is there is a problem, otherwise returns value 1572 | 1573 | Args: 1574 | input_value (any): the data to validate/convert 1575 | required_types_mixed (list/dict/tuple): A list of 1576 | valid classes, or a list tuples of valid classes, or a dict where 1577 | the value is a tuple of value classes 1578 | path_to_item: (list) the path to the data being validated 1579 | this stores a list of keys or indices to get to the data being 1580 | validated 1581 | spec_property_naming (bool): True if the variable names in the input 1582 | data are serialized names as specified in the OpenAPI document. 1583 | False if the variables names in the input data are python 1584 | variable names in PEP-8 snake case. 1585 | _check_type: (boolean) if true, type will be checked and conversion 1586 | will be attempted. 1587 | configuration: (Configuration): the configuration class to use 1588 | when converting file_type items. 1589 | If passed, conversion will be attempted when possible 1590 | If not passed, no conversions will be attempted and 1591 | exceptions will be raised 1592 | 1593 | Returns: 1594 | the correctly typed value 1595 | 1596 | Raises: 1597 | ApiTypeError 1598 | """ 1599 | results = get_required_type_classes(required_types_mixed, spec_property_naming) 1600 | valid_classes, child_req_types_by_current_type = results 1601 | 1602 | input_class_simple = get_simple_class(input_value) 1603 | valid_type = is_valid_type(input_class_simple, valid_classes) 1604 | if not valid_type: 1605 | if configuration: 1606 | # if input_value is not valid_type try to convert it 1607 | converted_instance = attempt_convert_item( 1608 | input_value, 1609 | valid_classes, 1610 | path_to_item, 1611 | configuration, 1612 | spec_property_naming, 1613 | key_type=False, 1614 | must_convert=True, 1615 | check_type=_check_type, 1616 | ) 1617 | return converted_instance 1618 | else: 1619 | raise get_type_error( 1620 | input_value, path_to_item, valid_classes, key_type=False 1621 | ) 1622 | 1623 | # input_value's type is in valid_classes 1624 | if len(valid_classes) > 1 and configuration: 1625 | # there are valid classes which are not the current class 1626 | valid_classes_coercible = remove_uncoercible( 1627 | valid_classes, input_value, spec_property_naming, must_convert=False 1628 | ) 1629 | if valid_classes_coercible: 1630 | converted_instance = attempt_convert_item( 1631 | input_value, 1632 | valid_classes_coercible, 1633 | path_to_item, 1634 | configuration, 1635 | spec_property_naming, 1636 | key_type=False, 1637 | must_convert=False, 1638 | check_type=_check_type, 1639 | ) 1640 | return converted_instance 1641 | 1642 | if child_req_types_by_current_type == {}: 1643 | # all types are of the required types and there are no more inner 1644 | # variables left to look at 1645 | return input_value 1646 | inner_required_types = child_req_types_by_current_type.get(type(input_value)) 1647 | if inner_required_types is None: 1648 | # for this type, there are not more inner variables left to look at 1649 | return input_value 1650 | if isinstance(input_value, list): 1651 | if input_value == []: 1652 | # allow an empty list 1653 | return input_value 1654 | for index, inner_value in enumerate(input_value): 1655 | inner_path = list(path_to_item) 1656 | inner_path.append(index) 1657 | input_value[index] = validate_and_convert_types( 1658 | inner_value, 1659 | inner_required_types, 1660 | inner_path, 1661 | spec_property_naming, 1662 | _check_type, 1663 | configuration=configuration, 1664 | ) 1665 | elif isinstance(input_value, dict): 1666 | if input_value == {}: 1667 | # allow an empty dict 1668 | return input_value 1669 | for inner_key, inner_val in input_value.items(): 1670 | inner_path = list(path_to_item) 1671 | inner_path.append(inner_key) 1672 | if get_simple_class(inner_key) != str: 1673 | raise get_type_error( 1674 | inner_key, inner_path, valid_classes, key_type=True 1675 | ) 1676 | input_value[inner_key] = validate_and_convert_types( 1677 | inner_val, 1678 | inner_required_types, 1679 | inner_path, 1680 | spec_property_naming, 1681 | _check_type, 1682 | configuration=configuration, 1683 | ) 1684 | return input_value 1685 | 1686 | 1687 | def model_to_dict(model_instance, serialize=True): 1688 | """Returns the model properties as a dict 1689 | 1690 | Args: 1691 | model_instance (one of your model instances): the model instance that 1692 | will be converted to a dict. 1693 | 1694 | Keyword Args: 1695 | serialize (bool): if True, the keys in the dict will be values from 1696 | attribute_map 1697 | """ 1698 | result = {} 1699 | extract_item = ( 1700 | lambda item: (item[0], model_to_dict(item[1], serialize=serialize)) 1701 | if hasattr(item[1], "_data_store") 1702 | else item 1703 | ) 1704 | 1705 | model_instances = [model_instance] 1706 | if model_instance._composed_schemas: 1707 | model_instances.extend(model_instance._composed_instances) 1708 | seen_json_attribute_names = set() 1709 | used_fallback_python_attribute_names = set() 1710 | py_to_json_map = {} 1711 | for model_instance in model_instances: 1712 | for attr, value in model_instance._data_store.items(): 1713 | if serialize: 1714 | # we use get here because additional property key names do not 1715 | # exist in attribute_map 1716 | try: 1717 | attr = model_instance.attribute_map[attr] 1718 | py_to_json_map.update(model_instance.attribute_map) 1719 | seen_json_attribute_names.add(attr) 1720 | except KeyError: 1721 | used_fallback_python_attribute_names.add(attr) 1722 | if isinstance(value, list): 1723 | if not value: 1724 | # empty list or None 1725 | result[attr] = value 1726 | else: 1727 | res = [] 1728 | for v in value: 1729 | if isinstance(v, PRIMITIVE_TYPES) or v is None: 1730 | res.append(v) 1731 | elif isinstance(v, ModelSimple): 1732 | res.append(v.value) 1733 | elif isinstance(v, dict): 1734 | res.append(dict(map(extract_item, v.items()))) 1735 | else: 1736 | res.append(model_to_dict(v, serialize=serialize)) 1737 | result[attr] = res 1738 | elif isinstance(value, dict): 1739 | result[attr] = dict(map(extract_item, value.items())) 1740 | elif isinstance(value, ModelSimple): 1741 | result[attr] = value.value 1742 | elif hasattr(value, "_data_store"): 1743 | result[attr] = model_to_dict(value, serialize=serialize) 1744 | else: 1745 | result[attr] = value 1746 | if serialize: 1747 | for python_key in used_fallback_python_attribute_names: 1748 | json_key = py_to_json_map.get(python_key) 1749 | if json_key is None: 1750 | continue 1751 | if python_key == json_key: 1752 | continue 1753 | json_key_assigned_no_need_for_python_key = ( 1754 | json_key in seen_json_attribute_names 1755 | ) 1756 | if json_key_assigned_no_need_for_python_key: 1757 | del result[python_key] 1758 | 1759 | return result 1760 | 1761 | 1762 | def type_error_message( 1763 | var_value=None, var_name=None, valid_classes=None, key_type=None 1764 | ): 1765 | """ 1766 | Keyword Args: 1767 | var_value (any): the variable which has the type_error 1768 | var_name (str): the name of the variable which has the typ error 1769 | valid_classes (tuple): the accepted classes for current_item's 1770 | value 1771 | key_type (bool): False if our value is a value in a dict 1772 | True if it is a key in a dict 1773 | False if our item is an item in a list 1774 | """ 1775 | key_or_value = "value" 1776 | if key_type: 1777 | key_or_value = "key" 1778 | valid_classes_phrase = get_valid_classes_phrase(valid_classes) 1779 | msg = ( 1780 | "Invalid type for variable '{0}'. Required {1} type {2} and " 1781 | "passed type was {3}".format( 1782 | var_name, 1783 | key_or_value, 1784 | valid_classes_phrase, 1785 | type(var_value).__name__, 1786 | ) 1787 | ) 1788 | return msg 1789 | 1790 | 1791 | def get_valid_classes_phrase(input_classes): 1792 | """Returns a string phrase describing what types are allowed""" 1793 | all_classes = list(input_classes) 1794 | all_classes = sorted(all_classes, key=lambda cls: cls.__name__) 1795 | all_class_names = [cls.__name__ for cls in all_classes] 1796 | if len(all_class_names) == 1: 1797 | return "is {0}".format(all_class_names[0]) 1798 | return "is one of [{0}]".format(", ".join(all_class_names)) 1799 | 1800 | 1801 | def get_allof_instances(self, model_args, constant_args): 1802 | """ 1803 | Args: 1804 | self: the class we are handling 1805 | model_args (dict): var_name to var_value 1806 | used to make instances 1807 | constant_args (dict): 1808 | metadata arguments: 1809 | _check_type 1810 | _path_to_item 1811 | _spec_property_naming 1812 | _configuration 1813 | _visited_composed_classes 1814 | 1815 | Returns 1816 | composed_instances (list) 1817 | """ 1818 | composed_instances = [] 1819 | for allof_class in self._composed_schemas["allOf"]: 1820 | 1821 | try: 1822 | if constant_args.get("_spec_property_naming"): 1823 | allof_instance = allof_class._from_openapi_data( 1824 | **model_args, **constant_args 1825 | ) 1826 | else: 1827 | allof_instance = allof_class(**model_args, **constant_args) 1828 | composed_instances.append(allof_instance) 1829 | except Exception as ex: 1830 | raise ApiValueError( 1831 | "Invalid inputs given to generate an instance of '%s'. The " 1832 | "input data was invalid for the allOf schema '%s' in the composed " 1833 | "schema '%s'. Error=%s" 1834 | % ( 1835 | allof_class.__name__, 1836 | allof_class.__name__, 1837 | self.__class__.__name__, 1838 | str(ex), 1839 | ) 1840 | ) from ex 1841 | return composed_instances 1842 | 1843 | 1844 | def get_oneof_instance(cls, model_kwargs, constant_kwargs, model_arg=None): 1845 | """ 1846 | Find the oneOf schema that matches the input data (e.g. payload). 1847 | If exactly one schema matches the input data, an instance of that schema 1848 | is returned. 1849 | If zero or more than one schema match the input data, an exception is raised. 1850 | In OAS 3.x, the payload MUST, by validation, match exactly one of the 1851 | schemas described by oneOf. 1852 | 1853 | Args: 1854 | cls: the class we are handling 1855 | model_kwargs (dict): var_name to var_value 1856 | The input data, e.g. the payload that must match a oneOf schema 1857 | in the OpenAPI document. 1858 | constant_kwargs (dict): var_name to var_value 1859 | args that every model requires, including configuration, server 1860 | and path to item. 1861 | 1862 | Kwargs: 1863 | model_arg: (int, float, bool, str, date, datetime, ModelSimple, None): 1864 | the value to assign to a primitive class or ModelSimple class 1865 | Notes: 1866 | - this is only passed in when oneOf includes types which are not object 1867 | - None is used to suppress handling of model_arg, nullable models are handled in __new__ 1868 | 1869 | Returns 1870 | oneof_instance (instance) 1871 | """ 1872 | if len(cls._composed_schemas["oneOf"]) == 0: 1873 | return None 1874 | 1875 | oneof_instances = [] 1876 | # Iterate over each oneOf schema and determine if the input data 1877 | # matches the oneOf schemas. 1878 | for oneof_class in cls._composed_schemas["oneOf"]: 1879 | # The composed oneOf schema allows the 'null' type and the input data 1880 | # is the null value. This is a OAS >= 3.1 feature. 1881 | if oneof_class is none_type: 1882 | # skip none_types because we are deserializing dict data. 1883 | # none_type deserialization is handled in the __new__ method 1884 | continue 1885 | 1886 | single_value_input = allows_single_value_input(oneof_class) 1887 | 1888 | try: 1889 | if not single_value_input: 1890 | if constant_kwargs.get("_spec_property_naming"): 1891 | oneof_instance = oneof_class._from_openapi_data( 1892 | **model_kwargs, **constant_kwargs 1893 | ) 1894 | else: 1895 | oneof_instance = oneof_class(**model_kwargs, **constant_kwargs) 1896 | else: 1897 | if issubclass(oneof_class, ModelSimple): 1898 | if constant_kwargs.get("_spec_property_naming"): 1899 | oneof_instance = oneof_class._from_openapi_data( 1900 | model_arg, **constant_kwargs 1901 | ) 1902 | else: 1903 | oneof_instance = oneof_class(model_arg, **constant_kwargs) 1904 | elif oneof_class in PRIMITIVE_TYPES: 1905 | oneof_instance = validate_and_convert_types( 1906 | model_arg, 1907 | (oneof_class,), 1908 | constant_kwargs["_path_to_item"], 1909 | constant_kwargs["_spec_property_naming"], 1910 | constant_kwargs["_check_type"], 1911 | configuration=constant_kwargs["_configuration"], 1912 | ) 1913 | oneof_instances.append(oneof_instance) 1914 | except Exception: 1915 | pass 1916 | if len(oneof_instances) == 0: 1917 | raise ApiValueError( 1918 | "Invalid inputs given to generate an instance of %s. None " 1919 | "of the oneOf schemas matched the input data." % cls.__name__ 1920 | ) 1921 | elif len(oneof_instances) > 1: 1922 | raise ApiValueError( 1923 | "Invalid inputs given to generate an instance of %s. Multiple " 1924 | "oneOf schemas matched the inputs, but a max of one is allowed." 1925 | % cls.__name__ 1926 | ) 1927 | return oneof_instances[0] 1928 | 1929 | 1930 | def get_anyof_instances(self, model_args, constant_args): 1931 | """ 1932 | Args: 1933 | self: the class we are handling 1934 | model_args (dict): var_name to var_value 1935 | The input data, e.g. the payload that must match at least one 1936 | anyOf child schema in the OpenAPI document. 1937 | constant_args (dict): var_name to var_value 1938 | args that every model requires, including configuration, server 1939 | and path to item. 1940 | 1941 | Returns 1942 | anyof_instances (list) 1943 | """ 1944 | anyof_instances = [] 1945 | if len(self._composed_schemas["anyOf"]) == 0: 1946 | return anyof_instances 1947 | 1948 | for anyof_class in self._composed_schemas["anyOf"]: 1949 | # The composed oneOf schema allows the 'null' type and the input data 1950 | # is the null value. This is a OAS >= 3.1 feature. 1951 | if anyof_class is none_type: 1952 | # skip none_types because we are deserializing dict data. 1953 | # none_type deserialization is handled in the __new__ method 1954 | continue 1955 | 1956 | try: 1957 | if constant_args.get("_spec_property_naming"): 1958 | anyof_instance = anyof_class._from_openapi_data( 1959 | **model_args, **constant_args 1960 | ) 1961 | else: 1962 | anyof_instance = anyof_class(**model_args, **constant_args) 1963 | anyof_instances.append(anyof_instance) 1964 | except Exception: 1965 | pass 1966 | if len(anyof_instances) == 0: 1967 | raise ApiValueError( 1968 | "Invalid inputs given to generate an instance of %s. None of the " 1969 | "anyOf schemas matched the inputs." % self.__class__.__name__ 1970 | ) 1971 | return anyof_instances 1972 | 1973 | 1974 | def get_discarded_args(self, composed_instances, model_args): 1975 | """ 1976 | Gathers the args that were discarded by configuration.discard_unknown_keys 1977 | """ 1978 | model_arg_keys = model_args.keys() 1979 | discarded_args = set() 1980 | # arguments passed to self were already converted to python names 1981 | # before __init__ was called 1982 | for instance in composed_instances: 1983 | if instance.__class__ in self._composed_schemas["allOf"]: 1984 | try: 1985 | keys = instance.to_dict().keys() 1986 | discarded_keys = model_args - keys 1987 | discarded_args.update(discarded_keys) 1988 | except Exception: 1989 | # allOf integer schema will throw exception 1990 | pass 1991 | else: 1992 | try: 1993 | all_keys = set(model_to_dict(instance, serialize=False).keys()) 1994 | js_keys = model_to_dict(instance, serialize=True).keys() 1995 | all_keys.update(js_keys) 1996 | discarded_keys = model_arg_keys - all_keys 1997 | discarded_args.update(discarded_keys) 1998 | except Exception: 1999 | # allOf integer schema will throw exception 2000 | pass 2001 | return discarded_args 2002 | 2003 | 2004 | def validate_get_composed_info(constant_args, model_args, self): 2005 | """ 2006 | For composed schemas, generate schema instances for 2007 | all schemas in the oneOf/anyOf/allOf definition. If additional 2008 | properties are allowed, also assign those properties on 2009 | all matched schemas that contain additionalProperties. 2010 | Openapi schemas are python classes. 2011 | 2012 | Exceptions are raised if: 2013 | - 0 or > 1 oneOf schema matches the model_args input data 2014 | - no anyOf schema matches the model_args input data 2015 | - any of the allOf schemas do not match the model_args input data 2016 | 2017 | Args: 2018 | constant_args (dict): these are the args that every model requires 2019 | model_args (dict): these are the required and optional spec args that 2020 | were passed in to make this model 2021 | self (class): the class that we are instantiating 2022 | This class contains self._composed_schemas 2023 | 2024 | Returns: 2025 | composed_info (list): length three 2026 | composed_instances (list): the composed instances which are not 2027 | self 2028 | var_name_to_model_instances (dict): a dict going from var_name 2029 | to the model_instance which holds that var_name 2030 | the model_instance may be self or an instance of one of the 2031 | classes in self.composed_instances() 2032 | additional_properties_model_instances (list): a list of the 2033 | model instances which have the property 2034 | additional_properties_type. This list can include self 2035 | """ 2036 | # create composed_instances 2037 | composed_instances = [] 2038 | allof_instances = get_allof_instances(self, model_args, constant_args) 2039 | composed_instances.extend(allof_instances) 2040 | oneof_instance = get_oneof_instance(self.__class__, model_args, constant_args) 2041 | if oneof_instance is not None: 2042 | composed_instances.append(oneof_instance) 2043 | anyof_instances = get_anyof_instances(self, model_args, constant_args) 2044 | composed_instances.extend(anyof_instances) 2045 | """ 2046 | set additional_properties_model_instances 2047 | additional properties must be evaluated at the schema level 2048 | so self's additional properties are most important 2049 | If self is a composed schema with: 2050 | - no properties defined in self 2051 | - additionalProperties: False 2052 | Then for object payloads every property is an additional property 2053 | and they are not allowed, so only empty dict is allowed 2054 | 2055 | Properties must be set on all matching schemas 2056 | so when a property is assigned toa composed instance, it must be set on all 2057 | composed instances regardless of additionalProperties presence 2058 | keeping it to prevent breaking changes in v5.0.1 2059 | TODO remove cls._additional_properties_model_instances in 6.0.0 2060 | """ 2061 | additional_properties_model_instances = [] 2062 | if self.additional_properties_type is not None: 2063 | additional_properties_model_instances = [self] 2064 | 2065 | """ 2066 | no need to set properties on self in here, they will be set in __init__ 2067 | By here all composed schema oneOf/anyOf/allOf instances have their properties set using 2068 | model_args 2069 | """ 2070 | discarded_args = get_discarded_args(self, composed_instances, model_args) 2071 | 2072 | # map variable names to composed_instances 2073 | var_name_to_model_instances = {} 2074 | for prop_name in model_args: 2075 | if prop_name not in discarded_args: 2076 | var_name_to_model_instances[prop_name] = [self] + composed_instances 2077 | 2078 | return [ 2079 | composed_instances, 2080 | var_name_to_model_instances, 2081 | additional_properties_model_instances, 2082 | discarded_args, 2083 | ] 2084 | -------------------------------------------------------------------------------- /python-client/deutschland/risikogebiete/models/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | # import all models into this package 4 | # if you have many models here with many references from one model to another this may 5 | # raise a RecursionError 6 | # to avoid this, import only the models that you directly need like: 7 | # from from deutschland.risikogebiete.model.pet import Pet 8 | # or import this package, but before doing it, use: 9 | # import sys 10 | # sys.setrecursionlimit(n) 11 | 12 | from deutschland.risikogebiete.model.risk_countries import RiskCountries 13 | -------------------------------------------------------------------------------- /python-client/deutschland/risikogebiete/rest.py: -------------------------------------------------------------------------------- 1 | """ 2 | Robert Koch Institut: Corona Risikogebiete API 3 | 4 | Aktuelle Corona Risikogebietsinformationen als API # noqa: E501 5 | 6 | The version of the OpenAPI document: 1.0.0 7 | Contact: kontakt@bund.dev 8 | Generated by: https://openapi-generator.tech 9 | """ 10 | 11 | 12 | import io 13 | import ipaddress 14 | import json 15 | import logging 16 | import re 17 | import ssl 18 | from urllib.parse import urlencode, urlparse 19 | from urllib.request import proxy_bypass_environment 20 | 21 | import urllib3 22 | from deutschland.risikogebiete.exceptions import ( 23 | ApiException, 24 | ApiValueError, 25 | ForbiddenException, 26 | NotFoundException, 27 | ServiceException, 28 | UnauthorizedException, 29 | ) 30 | 31 | logger = logging.getLogger(__name__) 32 | 33 | 34 | class RESTResponse(io.IOBase): 35 | def __init__(self, resp): 36 | self.urllib3_response = resp 37 | self.status = resp.status 38 | self.reason = resp.reason 39 | self.data = resp.data 40 | 41 | def getheaders(self): 42 | """Returns a dictionary of the response headers.""" 43 | return self.urllib3_response.getheaders() 44 | 45 | def getheader(self, name, default=None): 46 | """Returns a given response header.""" 47 | return self.urllib3_response.getheader(name, default) 48 | 49 | 50 | class RESTClientObject(object): 51 | def __init__(self, configuration, pools_size=4, maxsize=None): 52 | # urllib3.PoolManager will pass all kw parameters to connectionpool 53 | # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # noqa: E501 54 | # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # noqa: E501 55 | # maxsize is the number of requests to host that are allowed in parallel # noqa: E501 56 | # Custom SSL certificates and client certificates: http://urllib3.readthedocs.io/en/latest/advanced-usage.html # noqa: E501 57 | 58 | # cert_reqs 59 | if configuration.verify_ssl: 60 | cert_reqs = ssl.CERT_REQUIRED 61 | else: 62 | cert_reqs = ssl.CERT_NONE 63 | 64 | addition_pool_args = {} 65 | if configuration.assert_hostname is not None: 66 | addition_pool_args[ 67 | "assert_hostname" 68 | ] = configuration.assert_hostname # noqa: E501 69 | 70 | if configuration.retries is not None: 71 | addition_pool_args["retries"] = configuration.retries 72 | 73 | if configuration.socket_options is not None: 74 | addition_pool_args["socket_options"] = configuration.socket_options 75 | 76 | if maxsize is None: 77 | if configuration.connection_pool_maxsize is not None: 78 | maxsize = configuration.connection_pool_maxsize 79 | else: 80 | maxsize = 4 81 | 82 | # https pool manager 83 | if configuration.proxy and not should_bypass_proxies( 84 | configuration.host, no_proxy=configuration.no_proxy or "" 85 | ): 86 | self.pool_manager = urllib3.ProxyManager( 87 | num_pools=pools_size, 88 | maxsize=maxsize, 89 | cert_reqs=cert_reqs, 90 | ca_certs=configuration.ssl_ca_cert, 91 | cert_file=configuration.cert_file, 92 | key_file=configuration.key_file, 93 | proxy_url=configuration.proxy, 94 | proxy_headers=configuration.proxy_headers, 95 | **addition_pool_args 96 | ) 97 | else: 98 | self.pool_manager = urllib3.PoolManager( 99 | num_pools=pools_size, 100 | maxsize=maxsize, 101 | cert_reqs=cert_reqs, 102 | ca_certs=configuration.ssl_ca_cert, 103 | cert_file=configuration.cert_file, 104 | key_file=configuration.key_file, 105 | **addition_pool_args 106 | ) 107 | 108 | def request( 109 | self, 110 | method, 111 | url, 112 | query_params=None, 113 | headers=None, 114 | body=None, 115 | post_params=None, 116 | _preload_content=True, 117 | _request_timeout=None, 118 | ): 119 | """Perform requests. 120 | 121 | :param method: http request method 122 | :param url: http request url 123 | :param query_params: query parameters in the url 124 | :param headers: http request headers 125 | :param body: request json body, for `application/json` 126 | :param post_params: request post parameters, 127 | `application/x-www-form-urlencoded` 128 | and `multipart/form-data` 129 | :param _preload_content: if False, the urllib3.HTTPResponse object will 130 | be returned without reading/decoding response 131 | data. Default is True. 132 | :param _request_timeout: timeout setting for this request. If one 133 | number provided, it will be total request 134 | timeout. It can also be a pair (tuple) of 135 | (connection, read) timeouts. 136 | """ 137 | method = method.upper() 138 | assert method in ["GET", "HEAD", "DELETE", "POST", "PUT", "PATCH", "OPTIONS"] 139 | 140 | if post_params and body: 141 | raise ApiValueError( 142 | "body parameter cannot be used with post_params parameter." 143 | ) 144 | 145 | post_params = post_params or {} 146 | headers = headers or {} 147 | 148 | timeout = None 149 | if _request_timeout: 150 | if isinstance(_request_timeout, (int, float)): # noqa: E501,F821 151 | timeout = urllib3.Timeout(total=_request_timeout) 152 | elif isinstance(_request_timeout, tuple) and len(_request_timeout) == 2: 153 | timeout = urllib3.Timeout( 154 | connect=_request_timeout[0], read=_request_timeout[1] 155 | ) 156 | 157 | try: 158 | # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` 159 | if method in ["POST", "PUT", "PATCH", "OPTIONS", "DELETE"]: 160 | # Only set a default Content-Type for POST, PUT, PATCH and OPTIONS requests 161 | if (method != "DELETE") and ("Content-Type" not in headers): 162 | headers["Content-Type"] = "application/json" 163 | if query_params: 164 | url += "?" + urlencode(query_params) 165 | if ("Content-Type" not in headers) or ( 166 | re.search("json", headers["Content-Type"], re.IGNORECASE) 167 | ): 168 | request_body = None 169 | if body is not None: 170 | request_body = json.dumps(body) 171 | r = self.pool_manager.request( 172 | method, 173 | url, 174 | body=request_body, 175 | preload_content=_preload_content, 176 | timeout=timeout, 177 | headers=headers, 178 | ) 179 | elif ( 180 | headers["Content-Type"] == "application/x-www-form-urlencoded" 181 | ): # noqa: E501 182 | r = self.pool_manager.request( 183 | method, 184 | url, 185 | fields=post_params, 186 | encode_multipart=False, 187 | preload_content=_preload_content, 188 | timeout=timeout, 189 | headers=headers, 190 | ) 191 | elif headers["Content-Type"] == "multipart/form-data": 192 | # must del headers['Content-Type'], or the correct 193 | # Content-Type which generated by urllib3 will be 194 | # overwritten. 195 | del headers["Content-Type"] 196 | r = self.pool_manager.request( 197 | method, 198 | url, 199 | fields=post_params, 200 | encode_multipart=True, 201 | preload_content=_preload_content, 202 | timeout=timeout, 203 | headers=headers, 204 | ) 205 | # Pass a `string` parameter directly in the body to support 206 | # other content types than Json when `body` argument is 207 | # provided in serialized form 208 | elif isinstance(body, str) or isinstance(body, bytes): 209 | request_body = body 210 | r = self.pool_manager.request( 211 | method, 212 | url, 213 | body=request_body, 214 | preload_content=_preload_content, 215 | timeout=timeout, 216 | headers=headers, 217 | ) 218 | else: 219 | # Cannot generate the request from given parameters 220 | msg = """Cannot prepare a request message for provided 221 | arguments. Please check that your arguments match 222 | declared content type.""" 223 | raise ApiException(status=0, reason=msg) 224 | # For `GET`, `HEAD` 225 | else: 226 | r = self.pool_manager.request( 227 | method, 228 | url, 229 | fields=query_params, 230 | preload_content=_preload_content, 231 | timeout=timeout, 232 | headers=headers, 233 | ) 234 | except urllib3.exceptions.SSLError as e: 235 | msg = "{0}\n{1}".format(type(e).__name__, str(e)) 236 | raise ApiException(status=0, reason=msg) 237 | 238 | if _preload_content: 239 | r = RESTResponse(r) 240 | 241 | # log response body 242 | logger.debug("response body: %s", r.data) 243 | 244 | if not 200 <= r.status <= 299: 245 | if r.status == 401: 246 | raise UnauthorizedException(http_resp=r) 247 | 248 | if r.status == 403: 249 | raise ForbiddenException(http_resp=r) 250 | 251 | if r.status == 404: 252 | raise NotFoundException(http_resp=r) 253 | 254 | if 500 <= r.status <= 599: 255 | raise ServiceException(http_resp=r) 256 | 257 | raise ApiException(http_resp=r) 258 | 259 | return r 260 | 261 | def GET( 262 | self, 263 | url, 264 | headers=None, 265 | query_params=None, 266 | _preload_content=True, 267 | _request_timeout=None, 268 | ): 269 | return self.request( 270 | "GET", 271 | url, 272 | headers=headers, 273 | _preload_content=_preload_content, 274 | _request_timeout=_request_timeout, 275 | query_params=query_params, 276 | ) 277 | 278 | def HEAD( 279 | self, 280 | url, 281 | headers=None, 282 | query_params=None, 283 | _preload_content=True, 284 | _request_timeout=None, 285 | ): 286 | return self.request( 287 | "HEAD", 288 | url, 289 | headers=headers, 290 | _preload_content=_preload_content, 291 | _request_timeout=_request_timeout, 292 | query_params=query_params, 293 | ) 294 | 295 | def OPTIONS( 296 | self, 297 | url, 298 | headers=None, 299 | query_params=None, 300 | post_params=None, 301 | body=None, 302 | _preload_content=True, 303 | _request_timeout=None, 304 | ): 305 | return self.request( 306 | "OPTIONS", 307 | url, 308 | headers=headers, 309 | query_params=query_params, 310 | post_params=post_params, 311 | _preload_content=_preload_content, 312 | _request_timeout=_request_timeout, 313 | body=body, 314 | ) 315 | 316 | def DELETE( 317 | self, 318 | url, 319 | headers=None, 320 | query_params=None, 321 | body=None, 322 | _preload_content=True, 323 | _request_timeout=None, 324 | ): 325 | return self.request( 326 | "DELETE", 327 | url, 328 | headers=headers, 329 | query_params=query_params, 330 | _preload_content=_preload_content, 331 | _request_timeout=_request_timeout, 332 | body=body, 333 | ) 334 | 335 | def POST( 336 | self, 337 | url, 338 | headers=None, 339 | query_params=None, 340 | post_params=None, 341 | body=None, 342 | _preload_content=True, 343 | _request_timeout=None, 344 | ): 345 | return self.request( 346 | "POST", 347 | url, 348 | headers=headers, 349 | query_params=query_params, 350 | post_params=post_params, 351 | _preload_content=_preload_content, 352 | _request_timeout=_request_timeout, 353 | body=body, 354 | ) 355 | 356 | def PUT( 357 | self, 358 | url, 359 | headers=None, 360 | query_params=None, 361 | post_params=None, 362 | body=None, 363 | _preload_content=True, 364 | _request_timeout=None, 365 | ): 366 | return self.request( 367 | "PUT", 368 | url, 369 | headers=headers, 370 | query_params=query_params, 371 | post_params=post_params, 372 | _preload_content=_preload_content, 373 | _request_timeout=_request_timeout, 374 | body=body, 375 | ) 376 | 377 | def PATCH( 378 | self, 379 | url, 380 | headers=None, 381 | query_params=None, 382 | post_params=None, 383 | body=None, 384 | _preload_content=True, 385 | _request_timeout=None, 386 | ): 387 | return self.request( 388 | "PATCH", 389 | url, 390 | headers=headers, 391 | query_params=query_params, 392 | post_params=post_params, 393 | _preload_content=_preload_content, 394 | _request_timeout=_request_timeout, 395 | body=body, 396 | ) 397 | 398 | 399 | # end of class RESTClientObject 400 | def is_ipv4(target): 401 | """Test if IPv4 address or not""" 402 | try: 403 | chk = ipaddress.IPv4Address(target) 404 | return True 405 | except ipaddress.AddressValueError: 406 | return False 407 | 408 | 409 | def in_ipv4net(target, net): 410 | """Test if target belongs to given IPv4 network""" 411 | try: 412 | nw = ipaddress.IPv4Network(net) 413 | ip = ipaddress.IPv4Address(target) 414 | if ip in nw: 415 | return True 416 | return False 417 | except ipaddress.AddressValueError: 418 | return False 419 | except ipaddress.NetmaskValueError: 420 | return False 421 | 422 | 423 | def should_bypass_proxies(url, no_proxy=None): 424 | """Yet another requests.should_bypass_proxies 425 | Test if proxies should not be used for a particular url. 426 | """ 427 | 428 | parsed = urlparse(url) 429 | 430 | # special cases 431 | if parsed.hostname in [None, ""]: 432 | return True 433 | 434 | # special cases 435 | if no_proxy in [None, ""]: 436 | return False 437 | if no_proxy == "*": 438 | return True 439 | 440 | no_proxy = no_proxy.lower().replace(" ", "") 441 | entries = (host for host in no_proxy.split(",") if host) 442 | 443 | if is_ipv4(parsed.hostname): 444 | for item in entries: 445 | if in_ipv4net(parsed.hostname, item): 446 | return True 447 | return proxy_bypass_environment(parsed.hostname, {"no": no_proxy}) 448 | -------------------------------------------------------------------------------- /python-client/docs/DefaultApi.md: -------------------------------------------------------------------------------- 1 | # risikogebiete.DefaultApi 2 | 3 | All URIs are relative to *https://api.einreiseanmeldung.de/reisendenportal* 4 | 5 | Method | HTTP request | Description 6 | ------------- | ------------- | ------------- 7 | [**risikogebiete_get**](DefaultApi.md#risikogebiete_get) | **GET** /risikogebiete | Liste der Länder 8 | 9 | 10 | # **risikogebiete_get** 11 | > RiskCountries risikogebiete_get() 12 | 13 | Liste der Länder 14 | 15 | Liste aller Länder mit Risikoeinstufung 16 | 17 | ### Example 18 | 19 | 20 | ```python 21 | import time 22 | from deutschland import risikogebiete 23 | from deutschland.risikogebiete.api import default_api 24 | from deutschland.risikogebiete.model.risk_countries import RiskCountries 25 | from pprint import pprint 26 | # Defining the host is optional and defaults to https://api.einreiseanmeldung.de/reisendenportal 27 | # See configuration.py for a list of all supported configuration parameters. 28 | configuration = risikogebiete.Configuration( 29 | host = "https://api.einreiseanmeldung.de/reisendenportal" 30 | ) 31 | 32 | 33 | # Enter a context with an instance of the API client 34 | with risikogebiete.ApiClient() as api_client: 35 | # Create an instance of the API class 36 | api_instance = default_api.DefaultApi(api_client) 37 | 38 | # example, this endpoint has no required or optional parameters 39 | try: 40 | # Liste der Länder 41 | api_response = api_instance.risikogebiete_get() 42 | pprint(api_response) 43 | except risikogebiete.ApiException as e: 44 | print("Exception when calling DefaultApi->risikogebiete_get: %s\n" % e) 45 | ``` 46 | 47 | 48 | ### Parameters 49 | This endpoint does not need any parameter. 50 | 51 | ### Return type 52 | 53 | [**RiskCountries**](RiskCountries.md) 54 | 55 | ### Authorization 56 | 57 | No authorization required 58 | 59 | ### HTTP request headers 60 | 61 | - **Content-Type**: Not defined 62 | - **Accept**: application/json 63 | 64 | 65 | ### HTTP response details 66 | 67 | | Status code | Description | Response headers | 68 | |-------------|-------------|------------------| 69 | **200** | OK | - | 70 | 71 | [[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) 72 | 73 | -------------------------------------------------------------------------------- /python-client/docs/RiskCountries.md: -------------------------------------------------------------------------------- 1 | # RiskCountries 2 | 3 | 4 | ## Properties 5 | Name | Type | Description | Notes 6 | ------------ | ------------- | ------------- | ------------- 7 | **value** | **[{str: (bool, date, datetime, dict, float, int, list, str, none_type)}]** | | 8 | 9 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 10 | 11 | 12 | -------------------------------------------------------------------------------- /python-client/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool] 2 | [tool.poetry] 3 | name = "de-risikogebiete" 4 | version = "0.1.0" 5 | description = "Robert Koch Institut: Corona Risikogebiete API" 6 | keywords = ["OpenAPI", "OpenAPI-Generator", "risikogebiete", "App", "API"] 7 | homepage = "https://github.com/bundesAPI/risikogebiete-api" 8 | authors = ["BundesAPI "] 9 | packages = [ 10 | { include = "deutschland"} 11 | ] 12 | license = "Apache-2.0" 13 | readme = "README.md" 14 | 15 | [tool.poetry.dependencies] 16 | python = ">=3.6" 17 | python-dateutil = "*" 18 | urllib3 = ">=1.25.3" 19 | 20 | [tool.poetry.urls] 21 | "Bug Tracker" = "https://github.com/bundesAPI/risikogebiete-api/issues" 22 | 23 | [tool.poetry.dev-dependencies] 24 | black = "^21.7b0" 25 | pytest = "^6.2.4" 26 | 27 | 28 | [build-system] 29 | requires = ["poetry-core>=1.0.0"] 30 | build-backend = "poetry.core.masonry.api" 31 | 32 | -------------------------------------------------------------------------------- /python-client/requirements.txt: -------------------------------------------------------------------------------- 1 | python_dateutil >= 2.5.3 2 | setuptools >= 59.0.0 3 | urllib3 >= 1.25.3 4 | -------------------------------------------------------------------------------- /python-client/sphinx-docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /python-client/sphinx-docs/conf.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | sys.path.insert(0, os.path.abspath("../")) 5 | # Configuration file for the Sphinx documentation builder. 6 | # 7 | # This file only contains a selection of the most common options. For a full 8 | # list see the documentation: 9 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 10 | 11 | # -- Path setup -------------------------------------------------------------- 12 | 13 | # If extensions (or modules to document with autodoc) are in another directory, 14 | # add these directories to sys.path here. If the directory is relative to the 15 | # documentation root, use os.path.abspath to make it absolute, like shown here. 16 | # 17 | # import os 18 | # import sys 19 | # sys.path.insert(0, os.path.abspath('.')) 20 | 21 | 22 | # -- Project information ----------------------------------------------------- 23 | 24 | project = "risikogebiete-api" 25 | copyright = "2022, BundesAPI" 26 | author = "BundesAPI" 27 | 28 | # The short X.Y version 29 | version = "0.1.0" 30 | 31 | # The full version, including alpha/beta/rc tags 32 | release = "0.1.0" 33 | 34 | 35 | # -- General configuration --------------------------------------------------- 36 | 37 | # Add any Sphinx extension module names here, as strings. They can be 38 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 39 | # ones. 40 | extensions = [ 41 | "m2r2", 42 | "sphinx.ext.autodoc", 43 | "sphinx.ext.napoleon", 44 | "sphinx.ext.autosummary", 45 | ] 46 | 47 | # Add any paths that contain templates here, relative to this directory. 48 | templates_path = ["_templates"] 49 | 50 | # The language for content autogenerated by Sphinx. Refer to documentation 51 | # for a list of supported languages. 52 | # 53 | # This is also used if you do content translation via gettext catalogs. 54 | # Usually you set "language" from the command line for these cases. 55 | language = "de" 56 | 57 | # List of patterns, relative to source directory, that match files and 58 | # directories to ignore when looking for source files. 59 | # This pattern also affects html_static_path and html_extra_path. 60 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 61 | 62 | 63 | # -- Options for HTML output ------------------------------------------------- 64 | 65 | # The theme to use for HTML and HTML Help pages. See the documentation for 66 | # a list of builtin themes. 67 | # 68 | html_theme = "alabaster" 69 | 70 | # Add any paths that contain custom static files (such as style sheets) here, 71 | # relative to this directory. They are copied after the builtin static files, 72 | # so a file named "default.css" will overwrite the builtin "default.css". 73 | html_static_path = ["_static"] 74 | 75 | 76 | # -- Extension configuration ------------------------------------------------- 77 | source_extensions = [".rst", ".md"] 78 | -------------------------------------------------------------------------------- /python-client/sphinx-docs/index.rst: -------------------------------------------------------------------------------- 1 | risikogebiete-api Documentation 2 | =============================== 3 | 4 | .. toctree:: 5 | :glob: 6 | 7 | source/* 8 | -------------------------------------------------------------------------------- /python-client/sphinx-docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /python-client/sphinx-docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | deutschland 2 | =========== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | risikogebiete 8 | -------------------------------------------------------------------------------- /python-client/sphinx-docs/source/risikogebiete.api.rst: -------------------------------------------------------------------------------- 1 | risikogebiete.api package 2 | ========================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | risikogebiete.api.default\_api module 8 | ------------------------------------- 9 | 10 | .. automodule:: risikogebiete.api.default_api 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | Module contents 16 | --------------- 17 | 18 | .. automodule:: risikogebiete.api 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | -------------------------------------------------------------------------------- /python-client/sphinx-docs/source/risikogebiete.apis.rst: -------------------------------------------------------------------------------- 1 | risikogebiete.apis package 2 | ========================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: risikogebiete.apis 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /python-client/sphinx-docs/source/risikogebiete.model.rst: -------------------------------------------------------------------------------- 1 | risikogebiete.model package 2 | =========================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | risikogebiete.model.risk\_countries module 8 | ------------------------------------------ 9 | 10 | .. automodule:: risikogebiete.model.risk_countries 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | Module contents 16 | --------------- 17 | 18 | .. automodule:: risikogebiete.model 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | -------------------------------------------------------------------------------- /python-client/sphinx-docs/source/risikogebiete.models.rst: -------------------------------------------------------------------------------- 1 | risikogebiete.models package 2 | ============================ 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: risikogebiete.models 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /python-client/sphinx-docs/source/risikogebiete.rst: -------------------------------------------------------------------------------- 1 | risikogebiete package 2 | ===================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | risikogebiete.api 11 | risikogebiete.apis 12 | risikogebiete.model 13 | risikogebiete.models 14 | 15 | Submodules 16 | ---------- 17 | 18 | risikogebiete.api\_client module 19 | -------------------------------- 20 | 21 | .. automodule:: risikogebiete.api_client 22 | :members: 23 | :undoc-members: 24 | :show-inheritance: 25 | 26 | risikogebiete.configuration module 27 | ---------------------------------- 28 | 29 | .. automodule:: risikogebiete.configuration 30 | :members: 31 | :undoc-members: 32 | :show-inheritance: 33 | 34 | risikogebiete.exceptions module 35 | ------------------------------- 36 | 37 | .. automodule:: risikogebiete.exceptions 38 | :members: 39 | :undoc-members: 40 | :show-inheritance: 41 | 42 | risikogebiete.model\_utils module 43 | --------------------------------- 44 | 45 | .. automodule:: risikogebiete.model_utils 46 | :members: 47 | :undoc-members: 48 | :show-inheritance: 49 | 50 | risikogebiete.rest module 51 | ------------------------- 52 | 53 | .. automodule:: risikogebiete.rest 54 | :members: 55 | :undoc-members: 56 | :show-inheritance: 57 | 58 | Module contents 59 | --------------- 60 | 61 | .. automodule:: risikogebiete 62 | :members: 63 | :undoc-members: 64 | :show-inheritance: 65 | -------------------------------------------------------------------------------- /python-client/test-requirements.txt: -------------------------------------------------------------------------------- 1 | pytest-cov>=2.8.1 2 | -------------------------------------------------------------------------------- /python-client/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bundesAPI/risikogebiete-api/ed699c6d4b54446679dd37a3247bb15b12b7dad8/python-client/test/__init__.py -------------------------------------------------------------------------------- /python-client/test/test_default_api.py: -------------------------------------------------------------------------------- 1 | """ 2 | Robert Koch Institut: Corona Risikogebiete API 3 | 4 | Aktuelle Corona Risikogebietsinformationen als API # noqa: E501 5 | 6 | The version of the OpenAPI document: 1.0.0 7 | Contact: kontakt@bund.dev 8 | Generated by: https://openapi-generator.tech 9 | """ 10 | 11 | 12 | import unittest 13 | 14 | from deutschland.risikogebiete.api.default_api import DefaultApi # noqa: E501 15 | 16 | from deutschland import risikogebiete 17 | 18 | 19 | class TestDefaultApi(unittest.TestCase): 20 | """DefaultApi unit test stubs""" 21 | 22 | def setUp(self): 23 | self.api = DefaultApi() # noqa: E501 24 | 25 | def tearDown(self): 26 | pass 27 | 28 | def test_risikogebiete_get(self): 29 | """Test case for risikogebiete_get 30 | 31 | Liste der Länder # noqa: E501 32 | """ 33 | pass 34 | 35 | 36 | if __name__ == "__main__": 37 | unittest.main() 38 | -------------------------------------------------------------------------------- /python-client/test/test_risk_countries.py: -------------------------------------------------------------------------------- 1 | """ 2 | Robert Koch Institut: Corona Risikogebiete API 3 | 4 | Aktuelle Corona Risikogebietsinformationen als API # noqa: E501 5 | 6 | The version of the OpenAPI document: 1.0.0 7 | Contact: kontakt@bund.dev 8 | Generated by: https://openapi-generator.tech 9 | """ 10 | 11 | 12 | import sys 13 | import unittest 14 | 15 | from deutschland.risikogebiete.model.risk_countries import RiskCountries 16 | 17 | from deutschland import risikogebiete 18 | 19 | 20 | class TestRiskCountries(unittest.TestCase): 21 | """RiskCountries unit test stubs""" 22 | 23 | def setUp(self): 24 | pass 25 | 26 | def tearDown(self): 27 | pass 28 | 29 | def testRiskCountries(self): 30 | """Test RiskCountries""" 31 | # FIXME: construct object with mandatory attributes with example values 32 | # model = RiskCountries() # noqa: E501 33 | pass 34 | 35 | 36 | if __name__ == "__main__": 37 | unittest.main() 38 | -------------------------------------------------------------------------------- /python-client/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py3 3 | 4 | [testenv] 5 | deps=-r{toxinidir}/requirements.txt 6 | -r{toxinidir}/test-requirements.txt 7 | 8 | commands= 9 | pytest --cov=risikogebiete 10 | --------------------------------------------------------------------------------