├── .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 |
--------------------------------------------------------------------------------