diff --git a/python-client/.openapi-generator-ignore b/python-client/.openapi-generator-ignore new file mode 100644 index 0000000..7484ee5 --- /dev/null +++ b/python-client/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/python-client/.openapi-generator/FILES b/python-client/.openapi-generator/FILES new file mode 100644 index 0000000..804920d --- /dev/null +++ b/python-client/.openapi-generator/FILES @@ -0,0 +1,29 @@ +.gitignore +.gitlab-ci.yml +.openapi-generator-ignore +.travis.yml +README.md +bundestag_tagesordnung/__init__.py +bundestag_tagesordnung/api/__init__.py +bundestag_tagesordnung/api/default_api.py +bundestag_tagesordnung/api_client.py +bundestag_tagesordnung/apis/__init__.py +bundestag_tagesordnung/configuration.py +bundestag_tagesordnung/exceptions.py +bundestag_tagesordnung/model/__init__.py +bundestag_tagesordnung/model_utils.py +bundestag_tagesordnung/models/__init__.py +bundestag_tagesordnung/rest.py +create_doc.py +docs/DefaultApi.md +git_push.sh +pyproject.toml +rename_generated_code.py +requirements.txt +requirements.txt +setup.cfg +setup.py +test-requirements.txt +test/__init__.py +test/test_default_api.py +tox.ini diff --git a/python-client/.openapi-generator/VERSION b/python-client/.openapi-generator/VERSION new file mode 100644 index 0000000..358e78e --- /dev/null +++ b/python-client/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.1.0 \ No newline at end of file diff --git a/python-client/README.md b/python-client/README.md new file mode 100644 index 0000000..e4b93fb --- /dev/null +++ b/python-client/README.md @@ -0,0 +1,119 @@ +# bundestag-tagesordnung +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +This Python package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: + +- API version: 1.0.0 +- Package version: 1.0.0 +- Build package: org.openapitools.codegen.languages.PythonClientCodegen + +## Requirements. + +Python >= 3.6 + +## Installation & Usage +### pip install + +```sh +pip install deutschland[bundestag_tagesordnung] +``` + +### poetry install + +```sh +poetry add deutschland -E bundestag_tagesordnung +``` + +### Setuptools + +Install via [Setuptools](http://pypi.python.org/pypi/setuptools). + +```sh +python setup.py install --user +``` +(or `sudo python setup.py install` to install the package for all users) + +## Usage + +Import the package: +```python +from deutschland import bundestag_tagesordnung +``` + +## Getting Started + +Please follow the [installation procedure](#installation--usage) and then run the following: + +```python + +import time +from deutschland import bundestag_tagesordnung +from pprint import pprint +from deutschland.bundestag_tagesordnung.api import default_api +# Defining the host is optional and defaults to https://api.hutt.io +# See configuration.py for a list of all supported configuration parameters. +configuration = bundestag_tagesordnung.Configuration( + host = "https://api.hutt.io" +) + + + +# Enter a context with an instance of the API client +with bundestag_tagesordnung.ApiClient(configuration) as api_client: + # Create an instance of the API class + api_instance = default_api.DefaultApi(api_client) + year = 1 # int | Das Jahr, für das Tagesordnungen abgerufen werden sollen (optional) (optional) + week = 1 # int | Die Kalenderwoche, für die Tagesordnungen abgerufen werden sollen (optional; kann mit Jahr kombiniert werden) (optional) + + try: + # Tagesordnungen im CSV-Format abrufen + api_response = api_instance.bt_to_csv_get(year=year, week=week) + pprint(api_response) + except bundestag_tagesordnung.ApiException as e: + print("Exception when calling DefaultApi->bt_to_csv_get: %s\n" % e) +``` + +## Documentation for API Endpoints + +All URIs are relative to *https://api.hutt.io* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*DefaultApi* | [**bt_to_csv_get**](docs/DefaultApi.md#bt_to_csv_get) | **GET** /bt-to/csv | Tagesordnungen im CSV-Format abrufen +*DefaultApi* | [**bt_to_ical_get**](docs/DefaultApi.md#bt_to_ical_get) | **GET** /bt-to/ical | Tagesordnungen im iCal-Format abrufen +*DefaultApi* | [**bt_to_json_get**](docs/DefaultApi.md#bt_to_json_get) | **GET** /bt-to/json | Tagesordnungen im JSON-Format abrufen +*DefaultApi* | [**bt_to_xml_get**](docs/DefaultApi.md#bt_to_xml_get) | **GET** /bt-to/xml | Tagesordnungen im XML-Format abrufen + + +## Documentation For Models + + + +## Documentation For Authorization + + All endpoints do not require authorization. + +## Author + +kontakt@bund.dev + + +## Notes for Large OpenAPI documents +If the OpenAPI document is large, imports in bundestag_tagesordnung.apis and bundestag_tagesordnung.models may fail with a +RecursionError indicating the maximum recursion limit has been exceeded. In that case, there are a couple of solutions: + +Solution 1: +Use specific imports for apis and models like: +- `from deutschland.bundestag_tagesordnung.api.default_api import DefaultApi` +- `from deutschland.bundestag_tagesordnung.model.pet import Pet` + +Solution 2: +Before importing the package, adjust the maximum recursion limit as shown below: +``` +import sys +sys.setrecursionlimit(1500) +from deutschland import bundestag_tagesordnung +from deutschland.bundestag_tagesordnung.apis import * +from deutschland.bundestag_tagesordnung.models import * +``` + diff --git a/python-client/deutschland/bundestag_tagesordnung/__init__.py b/python-client/deutschland/bundestag_tagesordnung/__init__.py new file mode 100644 index 0000000..9d77a52 --- /dev/null +++ b/python-client/deutschland/bundestag_tagesordnung/__init__.py @@ -0,0 +1,30 @@ +# flake8: noqa + +""" + Tagesordnungen API + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Contact: kontakt@bund.dev + Generated by: https://openapi-generator.tech +""" + + +__version__ = "1.0.0" + +# import ApiClient +from deutschland.bundestag_tagesordnung.api_client import ApiClient + +# import Configuration +from deutschland.bundestag_tagesordnung.configuration import Configuration + +# import exceptions +from deutschland.bundestag_tagesordnung.exceptions import ( + ApiAttributeError, + ApiException, + ApiKeyError, + ApiTypeError, + ApiValueError, + OpenApiException, +) diff --git a/python-client/deutschland/bundestag_tagesordnung/api/__init__.py b/python-client/deutschland/bundestag_tagesordnung/api/__init__.py new file mode 100644 index 0000000..218ab21 --- /dev/null +++ b/python-client/deutschland/bundestag_tagesordnung/api/__init__.py @@ -0,0 +1,3 @@ +# do not import all apis into this module because that uses a lot of memory and stack frames +# if you need the ability to import all apis from one package, import them with +# from deutschland.bundestag_tagesordnung.apis import DefaultApi diff --git a/python-client/deutschland/bundestag_tagesordnung/api/default_api.py b/python-client/deutschland/bundestag_tagesordnung/api/default_api.py new file mode 100644 index 0000000..538eb93 --- /dev/null +++ b/python-client/deutschland/bundestag_tagesordnung/api/default_api.py @@ -0,0 +1,465 @@ +""" + Tagesordnungen API + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Contact: kontakt@bund.dev + Generated by: https://openapi-generator.tech +""" + +import re # noqa: F401 +import sys # noqa: F401 + +from deutschland.bundestag_tagesordnung.api_client import ApiClient +from deutschland.bundestag_tagesordnung.api_client import Endpoint as _Endpoint +from deutschland.bundestag_tagesordnung.model_utils import ( # noqa: F401 + check_allowed_values, + check_validations, + date, + datetime, + file_type, + none_type, + validate_and_convert_types, +) + + +class DefaultApi(object): + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + self.bt_to_csv_get_endpoint = _Endpoint( + settings={ + "response_type": (str,), + "auth": [], + "endpoint_path": "/bt-to/csv", + "operation_id": "bt_to_csv_get", + "http_method": "GET", + "servers": None, + }, + params_map={ + "all": [ + "year", + "week", + ], + "required": [], + "nullable": [], + "enum": [], + "validation": [], + }, + root_map={ + "validations": {}, + "allowed_values": {}, + "openapi_types": { + "year": (int,), + "week": (int,), + }, + "attribute_map": { + "year": "year", + "week": "week", + }, + "location_map": { + "year": "query", + "week": "query", + }, + "collection_format_map": {}, + }, + headers_map={ + "accept": ["text/csv"], + "content_type": [], + }, + api_client=api_client, + ) + self.bt_to_ical_get_endpoint = _Endpoint( + settings={ + "response_type": (str,), + "auth": [], + "endpoint_path": "/bt-to/ical", + "operation_id": "bt_to_ical_get", + "http_method": "GET", + "servers": None, + }, + params_map={ + "all": [ + "na", + "na_alarm", + "show_sw", + ], + "required": [], + "nullable": [], + "enum": [], + "validation": [], + }, + root_map={ + "validations": {}, + "allowed_values": {}, + "openapi_types": { + "na": (bool,), + "na_alarm": (bool,), + "show_sw": (bool,), + }, + "attribute_map": { + "na": "na", + "na_alarm": "naAlarm", + "show_sw": "showSW", + }, + "location_map": { + "na": "query", + "na_alarm": "query", + "show_sw": "query", + }, + "collection_format_map": {}, + }, + headers_map={ + "accept": ["text/calendar"], + "content_type": [], + }, + api_client=api_client, + ) + self.bt_to_json_get_endpoint = _Endpoint( + settings={ + "response_type": ( + [ + { + str: ( + bool, + date, + datetime, + dict, + float, + int, + list, + str, + none_type, + ) + } + ], + ), + "auth": [], + "endpoint_path": "/bt-to/json", + "operation_id": "bt_to_json_get", + "http_method": "GET", + "servers": None, + }, + params_map={ + "all": [ + "week", + ], + "required": [], + "nullable": [], + "enum": [], + "validation": [], + }, + root_map={ + "validations": {}, + "allowed_values": {}, + "openapi_types": { + "week": (int,), + }, + "attribute_map": { + "week": "week", + }, + "location_map": { + "week": "query", + }, + "collection_format_map": {}, + }, + headers_map={ + "accept": ["application/json"], + "content_type": [], + }, + api_client=api_client, + ) + self.bt_to_xml_get_endpoint = _Endpoint( + settings={ + "response_type": (str,), + "auth": [], + "endpoint_path": "/bt-to/xml", + "operation_id": "bt_to_xml_get", + "http_method": "GET", + "servers": None, + }, + params_map={ + "all": [ + "year", + "week", + ], + "required": [], + "nullable": [], + "enum": [], + "validation": [], + }, + root_map={ + "validations": {}, + "allowed_values": {}, + "openapi_types": { + "year": (int,), + "week": (int,), + }, + "attribute_map": { + "year": "year", + "week": "week", + }, + "location_map": { + "year": "query", + "week": "query", + }, + "collection_format_map": {}, + }, + headers_map={ + "accept": ["application/xml"], + "content_type": [], + }, + api_client=api_client, + ) + + def bt_to_csv_get(self, **kwargs): + """Tagesordnungen im CSV-Format abrufen # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.bt_to_csv_get(async_req=True) + >>> result = thread.get() + + + Keyword Args: + year (int): Das Jahr, für das Tagesordnungen abgerufen werden sollen (optional). [optional] + week (int): Die Kalenderwoche, für die Tagesordnungen abgerufen werden sollen (optional; kann mit Jahr kombiniert werden). [optional] + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (int/float/tuple): timeout setting for this request. If + one number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _content_type (str/None): force body content-type. + Default is None and content-type will be predicted by allowed + content-types and body. + _host_index (int/None): specifies the index of the server + that we want to use. + Default is read from the configuration. + _request_auths (list): set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + Default is None + async_req (bool): execute request asynchronously + + Returns: + str + If the method is called asynchronously, returns the request + thread. + """ + kwargs["async_req"] = kwargs.get("async_req", False) + kwargs["_return_http_data_only"] = kwargs.get("_return_http_data_only", True) + kwargs["_preload_content"] = kwargs.get("_preload_content", True) + kwargs["_request_timeout"] = kwargs.get("_request_timeout", None) + kwargs["_check_input_type"] = kwargs.get("_check_input_type", True) + kwargs["_check_return_type"] = kwargs.get("_check_return_type", True) + kwargs["_spec_property_naming"] = kwargs.get("_spec_property_naming", False) + kwargs["_content_type"] = kwargs.get("_content_type") + kwargs["_host_index"] = kwargs.get("_host_index") + kwargs["_request_auths"] = kwargs.get("_request_auths", None) + return self.bt_to_csv_get_endpoint.call_with_http_info(**kwargs) + + def bt_to_ical_get(self, **kwargs): + """Tagesordnungen im iCal-Format abrufen # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.bt_to_ical_get(async_req=True) + >>> result = thread.get() + + + Keyword Args: + na (bool): Zusätzliche Kalendereinträge für Namentliche Abstimmungen erstellen (optional; kombinierbar). [optional] + na_alarm (bool): Alarme 15 Minuten vor Namentlichen Abstimmungen hinzufügen (optional; kombinierbar). [optional] + show_sw (bool): Ganztägige Termine für Sitzungswochen erstellen (optional; kombinierbar). [optional] + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (int/float/tuple): timeout setting for this request. If + one number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _content_type (str/None): force body content-type. + Default is None and content-type will be predicted by allowed + content-types and body. + _host_index (int/None): specifies the index of the server + that we want to use. + Default is read from the configuration. + _request_auths (list): set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + Default is None + async_req (bool): execute request asynchronously + + Returns: + str + If the method is called asynchronously, returns the request + thread. + """ + kwargs["async_req"] = kwargs.get("async_req", False) + kwargs["_return_http_data_only"] = kwargs.get("_return_http_data_only", True) + kwargs["_preload_content"] = kwargs.get("_preload_content", True) + kwargs["_request_timeout"] = kwargs.get("_request_timeout", None) + kwargs["_check_input_type"] = kwargs.get("_check_input_type", True) + kwargs["_check_return_type"] = kwargs.get("_check_return_type", True) + kwargs["_spec_property_naming"] = kwargs.get("_spec_property_naming", False) + kwargs["_content_type"] = kwargs.get("_content_type") + kwargs["_host_index"] = kwargs.get("_host_index") + kwargs["_request_auths"] = kwargs.get("_request_auths", None) + return self.bt_to_ical_get_endpoint.call_with_http_info(**kwargs) + + def bt_to_json_get(self, **kwargs): + """Tagesordnungen im JSON-Format abrufen # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.bt_to_json_get(async_req=True) + >>> result = thread.get() + + + Keyword Args: + week (int): Die Kalenderwoche, für die Tagesordnungen abgerufen werden sollen (optional). [optional] + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (int/float/tuple): timeout setting for this request. If + one number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _content_type (str/None): force body content-type. + Default is None and content-type will be predicted by allowed + content-types and body. + _host_index (int/None): specifies the index of the server + that we want to use. + Default is read from the configuration. + _request_auths (list): set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + Default is None + async_req (bool): execute request asynchronously + + Returns: + [{str: (bool, date, datetime, dict, float, int, list, str, none_type)}] + If the method is called asynchronously, returns the request + thread. + """ + kwargs["async_req"] = kwargs.get("async_req", False) + kwargs["_return_http_data_only"] = kwargs.get("_return_http_data_only", True) + kwargs["_preload_content"] = kwargs.get("_preload_content", True) + kwargs["_request_timeout"] = kwargs.get("_request_timeout", None) + kwargs["_check_input_type"] = kwargs.get("_check_input_type", True) + kwargs["_check_return_type"] = kwargs.get("_check_return_type", True) + kwargs["_spec_property_naming"] = kwargs.get("_spec_property_naming", False) + kwargs["_content_type"] = kwargs.get("_content_type") + kwargs["_host_index"] = kwargs.get("_host_index") + kwargs["_request_auths"] = kwargs.get("_request_auths", None) + return self.bt_to_json_get_endpoint.call_with_http_info(**kwargs) + + def bt_to_xml_get(self, **kwargs): + """Tagesordnungen im XML-Format abrufen # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.bt_to_xml_get(async_req=True) + >>> result = thread.get() + + + Keyword Args: + year (int): Das Jahr, für das Tagesordnungen abgerufen werden sollen (optional). [optional] + week (int): Die Kalenderwoche, für die Tagesordnungen abgerufen werden sollen (optional; kann mit Jahr kombiniert werden). [optional] + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (int/float/tuple): timeout setting for this request. If + one number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _content_type (str/None): force body content-type. + Default is None and content-type will be predicted by allowed + content-types and body. + _host_index (int/None): specifies the index of the server + that we want to use. + Default is read from the configuration. + _request_auths (list): set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + Default is None + async_req (bool): execute request asynchronously + + Returns: + str + If the method is called asynchronously, returns the request + thread. + """ + kwargs["async_req"] = kwargs.get("async_req", False) + kwargs["_return_http_data_only"] = kwargs.get("_return_http_data_only", True) + kwargs["_preload_content"] = kwargs.get("_preload_content", True) + kwargs["_request_timeout"] = kwargs.get("_request_timeout", None) + kwargs["_check_input_type"] = kwargs.get("_check_input_type", True) + kwargs["_check_return_type"] = kwargs.get("_check_return_type", True) + kwargs["_spec_property_naming"] = kwargs.get("_spec_property_naming", False) + kwargs["_content_type"] = kwargs.get("_content_type") + kwargs["_host_index"] = kwargs.get("_host_index") + kwargs["_request_auths"] = kwargs.get("_request_auths", None) + return self.bt_to_xml_get_endpoint.call_with_http_info(**kwargs) diff --git a/python-client/deutschland/bundestag_tagesordnung/api_client.py b/python-client/deutschland/bundestag_tagesordnung/api_client.py new file mode 100644 index 0000000..02e8a78 --- /dev/null +++ b/python-client/deutschland/bundestag_tagesordnung/api_client.py @@ -0,0 +1,1015 @@ +""" + Tagesordnungen API + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Contact: kontakt@bund.dev + Generated by: https://openapi-generator.tech +""" + +import atexit +import io +import json +import mimetypes +import os +import re +import typing +from multiprocessing.pool import ThreadPool +from urllib.parse import quote + +from deutschland.bundestag_tagesordnung import rest +from deutschland.bundestag_tagesordnung.configuration import Configuration +from deutschland.bundestag_tagesordnung.exceptions import ( + ApiException, + ApiTypeError, + ApiValueError, +) +from deutschland.bundestag_tagesordnung.model_utils import ( + ModelComposed, + ModelNormal, + ModelSimple, + check_allowed_values, + check_validations, + date, + datetime, + deserialize_file, + file_type, + model_to_dict, + none_type, + validate_and_convert_types, +) +from urllib3.fields import RequestField + + +class ApiClient(object): + """Generic API client for OpenAPI client library builds. + + OpenAPI generic API client. This client handles the client- + server communication, and is invariant across implementations. Specifics of + the methods and models for each application are generated from the OpenAPI + templates. + + NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + Do not edit the class manually. + + :param configuration: .Configuration object for this client + :param header_name: a header to pass when making calls to the API. + :param header_value: a header value to pass when making calls to + the API. + :param cookie: a cookie to include in the header when making calls + to the API + :param pool_threads: The number of threads to use for async requests + to the API. More threads means more concurrent API requests. + """ + + _pool = None + + def __init__( + self, + configuration=None, + header_name=None, + header_value=None, + cookie=None, + pool_threads=1, + ): + if configuration is None: + configuration = Configuration.get_default_copy() + self.configuration = configuration + self.pool_threads = pool_threads + + self.rest_client = rest.RESTClientObject(configuration) + self.default_headers = {} + if header_name is not None: + self.default_headers[header_name] = header_value + self.cookie = cookie + # Set default User-Agent. + self.user_agent = "OpenAPI-Generator/1.0.0/python" + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + def close(self): + if self._pool: + self._pool.close() + self._pool.join() + self._pool = None + if hasattr(atexit, "unregister"): + atexit.unregister(self.close) + + @property + def pool(self): + """Create thread pool on first request + avoids instantiating unused threadpool for blocking clients. + """ + if self._pool is None: + atexit.register(self.close) + self._pool = ThreadPool(self.pool_threads) + return self._pool + + @property + def user_agent(self): + """User agent for this API client""" + return self.default_headers["User-Agent"] + + @user_agent.setter + def user_agent(self, value): + self.default_headers["User-Agent"] = value + + def set_default_header(self, header_name, header_value): + self.default_headers[header_name] = header_value + + def __call_api( + self, + resource_path: str, + method: str, + path_params: typing.Optional[typing.Dict[str, typing.Any]] = None, + query_params: typing.Optional[ + typing.List[typing.Tuple[str, typing.Any]] + ] = None, + header_params: typing.Optional[typing.Dict[str, typing.Any]] = None, + body: typing.Optional[typing.Any] = None, + post_params: typing.Optional[typing.List[typing.Tuple[str, typing.Any]]] = None, + files: typing.Optional[typing.Dict[str, typing.List[io.IOBase]]] = None, + response_type: typing.Optional[typing.Tuple[typing.Any]] = None, + auth_settings: typing.Optional[typing.List[str]] = None, + _return_http_data_only: typing.Optional[bool] = None, + collection_formats: typing.Optional[typing.Dict[str, str]] = None, + _preload_content: bool = True, + _request_timeout: typing.Optional[ + typing.Union[int, float, typing.Tuple] + ] = None, + _host: typing.Optional[str] = None, + _check_type: typing.Optional[bool] = None, + _content_type: typing.Optional[str] = None, + _request_auths: typing.Optional[ + typing.List[typing.Dict[str, typing.Any]] + ] = None, + ): + + config = self.configuration + + # header parameters + header_params = header_params or {} + header_params.update(self.default_headers) + if self.cookie: + header_params["Cookie"] = self.cookie + if header_params: + header_params = self.sanitize_for_serialization(header_params) + header_params = dict( + self.parameters_to_tuples(header_params, collection_formats) + ) + + # path parameters + if path_params: + path_params = self.sanitize_for_serialization(path_params) + path_params = self.parameters_to_tuples(path_params, collection_formats) + for k, v in path_params: + # specified safe chars, encode everything + resource_path = resource_path.replace( + "{%s}" % k, quote(str(v), safe=config.safe_chars_for_path_param) + ) + + # query parameters + if query_params: + query_params = self.sanitize_for_serialization(query_params) + query_params = self.parameters_to_tuples(query_params, collection_formats) + + # post parameters + if post_params or files: + post_params = post_params if post_params else [] + post_params = self.sanitize_for_serialization(post_params) + post_params = self.parameters_to_tuples(post_params, collection_formats) + post_params.extend(self.files_parameters(files)) + if header_params["Content-Type"].startswith("multipart"): + post_params = self.parameters_to_multipart(post_params, (dict)) + + # body + if body: + body = self.sanitize_for_serialization(body) + + # auth setting + self.update_params_for_auth( + header_params, + query_params, + auth_settings, + resource_path, + method, + body, + request_auths=_request_auths, + ) + + # request url + if _host is None: + url = self.configuration.host + resource_path + else: + # use server/host defined in path or operation instead + url = _host + resource_path + + try: + # perform request and return response + response_data = self.request( + method, + url, + query_params=query_params, + headers=header_params, + post_params=post_params, + body=body, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + ) + except ApiException as e: + e.body = e.body.decode("utf-8") + raise e + + self.last_response = response_data + + return_data = response_data + + if not _preload_content: + return return_data + return return_data + + # deserialize response data + if response_type: + if response_type != (file_type,): + encoding = "utf-8" + content_type = response_data.getheader("content-type") + if content_type is not None: + match = re.search(r"charset=([a-zA-Z\-\d]+)[\s\;]?", content_type) + if match: + encoding = match.group(1) + response_data.data = response_data.data.decode(encoding) + + return_data = self.deserialize(response_data, response_type, _check_type) + else: + return_data = None + + if _return_http_data_only: + return return_data + else: + return (return_data, response_data.status, response_data.getheaders()) + + def parameters_to_multipart(self, params, collection_types): + """Get parameters as list of tuples, formatting as json if value is collection_types + + :param params: Parameters as list of two-tuples + :param dict collection_types: Parameter collection types + :return: Parameters as list of tuple or urllib3.fields.RequestField + """ + new_params = [] + if collection_types is None: + collection_types = dict + for k, v in ( + params.items() if isinstance(params, dict) else params + ): # noqa: E501 + if isinstance( + v, collection_types + ): # v is instance of collection_type, formatting as application/json + v = json.dumps(v, ensure_ascii=False).encode("utf-8") + field = RequestField(k, v) + field.make_multipart(content_type="application/json; charset=utf-8") + new_params.append(field) + else: + new_params.append((k, v)) + return new_params + + @classmethod + def sanitize_for_serialization(cls, obj): + """Prepares data for transmission before it is sent with the rest client + If obj is None, return None. + If obj is str, int, long, float, bool, return directly. + If obj is datetime.datetime, datetime.date + convert to string in iso8601 format. + If obj is list, sanitize each element in the list. + If obj is dict, return the dict. + If obj is OpenAPI model, return the properties dict. + If obj is io.IOBase, return the bytes + :param obj: The data to serialize. + :return: The serialized form of data. + """ + if isinstance(obj, (ModelNormal, ModelComposed)): + return { + key: cls.sanitize_for_serialization(val) + for key, val in model_to_dict(obj, serialize=True).items() + } + elif isinstance(obj, io.IOBase): + return cls.get_file_data_and_close_file(obj) + elif isinstance(obj, (str, int, float, none_type, bool)): + return obj + elif isinstance(obj, (datetime, date)): + return obj.isoformat() + elif isinstance(obj, ModelSimple): + return cls.sanitize_for_serialization(obj.value) + elif isinstance(obj, (list, tuple)): + return [cls.sanitize_for_serialization(item) for item in obj] + if isinstance(obj, dict): + return { + key: cls.sanitize_for_serialization(val) for key, val in obj.items() + } + raise ApiValueError( + "Unable to prepare type {} for serialization".format(obj.__class__.__name__) + ) + + def deserialize(self, response, response_type, _check_type): + """Deserializes response into an object. + + :param response: RESTResponse object to be deserialized. + :param response_type: For the response, a tuple containing: + valid classes + a list containing valid classes (for list schemas) + a dict containing a tuple of valid classes as the value + Example values: + (str,) + (Pet,) + (float, none_type) + ([int, none_type],) + ({str: (bool, str, int, float, date, datetime, str, none_type)},) + :param _check_type: boolean, whether to check the types of the data + received from the server + :type _check_type: bool + + :return: deserialized object. + """ + # handle file downloading + # save response body into a tmp file and return the instance + if response_type == (file_type,): + content_disposition = response.getheader("Content-Disposition") + return deserialize_file( + response.data, + self.configuration, + content_disposition=content_disposition, + ) + + # fetch data from response object + try: + received_data = json.loads(response.data) + except ValueError: + received_data = response.data + + # store our data under the key of 'received_data' so users have some + # context if they are deserializing a string and the data type is wrong + deserialized_data = validate_and_convert_types( + received_data, + response_type, + ["received_data"], + True, + _check_type, + configuration=self.configuration, + ) + return deserialized_data + + def call_api( + self, + resource_path: str, + method: str, + path_params: typing.Optional[typing.Dict[str, typing.Any]] = None, + query_params: typing.Optional[ + typing.List[typing.Tuple[str, typing.Any]] + ] = None, + header_params: typing.Optional[typing.Dict[str, typing.Any]] = None, + body: typing.Optional[typing.Any] = None, + post_params: typing.Optional[typing.List[typing.Tuple[str, typing.Any]]] = None, + files: typing.Optional[typing.Dict[str, typing.List[io.IOBase]]] = None, + response_type: typing.Optional[typing.Tuple[typing.Any]] = None, + auth_settings: typing.Optional[typing.List[str]] = None, + async_req: typing.Optional[bool] = None, + _return_http_data_only: typing.Optional[bool] = None, + collection_formats: typing.Optional[typing.Dict[str, str]] = None, + _preload_content: bool = True, + _request_timeout: typing.Optional[ + typing.Union[int, float, typing.Tuple] + ] = None, + _host: typing.Optional[str] = None, + _check_type: typing.Optional[bool] = None, + _request_auths: typing.Optional[ + typing.List[typing.Dict[str, typing.Any]] + ] = None, + ): + """Makes the HTTP request (synchronous) and returns deserialized data. + + To make an async_req request, set the async_req parameter. + + :param resource_path: Path to method endpoint. + :param method: Method to call. + :param path_params: Path parameters in the url. + :param query_params: Query parameters in the url. + :param header_params: Header parameters to be + placed in the request header. + :param body: Request body. + :param post_params dict: Request post form parameters, + for `application/x-www-form-urlencoded`, `multipart/form-data`. + :param auth_settings list: Auth Settings names for the request. + :param response_type: For the response, a tuple containing: + valid classes + a list containing valid classes (for list schemas) + a dict containing a tuple of valid classes as the value + Example values: + (str,) + (Pet,) + (float, none_type) + ([int, none_type],) + ({str: (bool, str, int, float, date, datetime, str, none_type)},) + :param files: key -> field name, value -> a list of open file + objects for `multipart/form-data`. + :type files: dict + :param async_req bool: execute request asynchronously + :type async_req: bool, optional + :param _return_http_data_only: response data without head status code + and headers + :type _return_http_data_only: bool, optional + :param collection_formats: dict of collection formats for path, query, + header, and post parameters. + :type collection_formats: dict, optional + :param _preload_content: if False, the urllib3.HTTPResponse object will + be returned without reading/decoding response + data. Default is True. + :type _preload_content: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _check_type: boolean describing if the data back from the server + should have its type checked. + :type _check_type: bool, optional + :param _request_auths: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :type _request_auths: list, optional + :return: + If async_req parameter is True, + the request will be called asynchronously. + The method will return the request thread. + If parameter async_req is False or missing, + then the method will return the response directly. + """ + if not async_req: + return self.__call_api( + resource_path, + method, + path_params, + query_params, + header_params, + body, + post_params, + files, + response_type, + auth_settings, + _return_http_data_only, + collection_formats, + _preload_content, + _request_timeout, + _host, + _check_type, + _request_auths=_request_auths, + ) + + return self.pool.apply_async( + self.__call_api, + ( + resource_path, + method, + path_params, + query_params, + header_params, + body, + post_params, + files, + response_type, + auth_settings, + _return_http_data_only, + collection_formats, + _preload_content, + _request_timeout, + _host, + _check_type, + None, + _request_auths, + ), + ) + + def request( + self, + method, + url, + query_params=None, + headers=None, + post_params=None, + body=None, + _preload_content=True, + _request_timeout=None, + ): + """Makes the HTTP request using RESTClient.""" + if method == "GET": + return self.rest_client.GET( + url, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + headers=headers, + ) + elif method == "HEAD": + return self.rest_client.HEAD( + url, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + headers=headers, + ) + elif method == "OPTIONS": + return self.rest_client.OPTIONS( + url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body, + ) + elif method == "POST": + return self.rest_client.POST( + url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body, + ) + elif method == "PUT": + return self.rest_client.PUT( + url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body, + ) + elif method == "PATCH": + return self.rest_client.PATCH( + url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body, + ) + elif method == "DELETE": + return self.rest_client.DELETE( + url, + query_params=query_params, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body, + ) + else: + raise ApiValueError( + "http method must be `GET`, `HEAD`, `OPTIONS`," + " `POST`, `PATCH`, `PUT` or `DELETE`." + ) + + def parameters_to_tuples(self, params, collection_formats): + """Get parameters as list of tuples, formatting collections. + + :param params: Parameters as dict or list of two-tuples + :param dict collection_formats: Parameter collection formats + :return: Parameters as list of tuples, collections formatted + """ + new_params = [] + if collection_formats is None: + collection_formats = {} + for k, v in ( + params.items() if isinstance(params, dict) else params + ): # noqa: E501 + if k in collection_formats: + collection_format = collection_formats[k] + if collection_format == "multi": + new_params.extend((k, value) for value in v) + else: + if collection_format == "ssv": + delimiter = " " + elif collection_format == "tsv": + delimiter = "\t" + elif collection_format == "pipes": + delimiter = "|" + else: # csv is the default + delimiter = "," + new_params.append((k, delimiter.join(str(value) for value in v))) + else: + new_params.append((k, v)) + return new_params + + @staticmethod + def get_file_data_and_close_file(file_instance: io.IOBase) -> bytes: + file_data = file_instance.read() + file_instance.close() + return file_data + + def files_parameters( + self, files: typing.Optional[typing.Dict[str, typing.List[io.IOBase]]] = None + ): + """Builds form parameters. + + :param files: None or a dict with key=param_name and + value is a list of open file objects + :return: List of tuples of form parameters with file data + """ + if files is None: + return [] + + params = [] + for param_name, file_instances in files.items(): + if file_instances is None: + # if the file field is nullable, skip None values + continue + for file_instance in file_instances: + if file_instance is None: + # if the file field is nullable, skip None values + continue + if file_instance.closed is True: + raise ApiValueError( + "Cannot read a closed file. The passed in file_type " + "for %s must be open." % param_name + ) + filename = os.path.basename(file_instance.name) + filedata = self.get_file_data_and_close_file(file_instance) + mimetype = ( + mimetypes.guess_type(filename)[0] or "application/octet-stream" + ) + params.append( + tuple([param_name, tuple([filename, filedata, mimetype])]) + ) + + return params + + def select_header_accept(self, accepts): + """Returns `Accept` based on an array of accepts provided. + + :param accepts: List of headers. + :return: Accept (e.g. application/json). + """ + if not accepts: + return + + accepts = [x.lower() for x in accepts] + + if "application/json" in accepts: + return "application/json" + else: + return ", ".join(accepts) + + def select_header_content_type(self, content_types, method=None, body=None): + """Returns `Content-Type` based on an array of content_types provided. + + :param content_types: List of content-types. + :param method: http method (e.g. POST, PATCH). + :param body: http body to send. + :return: Content-Type (e.g. application/json). + """ + if not content_types: + return None + + content_types = [x.lower() for x in content_types] + + if ( + method == "PATCH" + and "application/json-patch+json" in content_types + and isinstance(body, list) + ): + return "application/json-patch+json" + + if "application/json" in content_types or "*/*" in content_types: + return "application/json" + else: + return content_types[0] + + def update_params_for_auth( + self, + headers, + queries, + auth_settings, + resource_path, + method, + body, + request_auths=None, + ): + """Updates header and query params based on authentication setting. + + :param headers: Header parameters dict to be updated. + :param queries: Query parameters tuple list to be updated. + :param auth_settings: Authentication setting identifiers list. + :param resource_path: A string representation of the HTTP request resource path. + :param method: A string representation of the HTTP request method. + :param body: A object representing the body of the HTTP request. + The object type is the return value of _encoder.default(). + :param request_auths: if set, the provided settings will + override the token in the configuration. + """ + if not auth_settings: + return + + if request_auths: + for auth_setting in request_auths: + self._apply_auth_params( + headers, queries, resource_path, method, body, auth_setting + ) + return + + for auth in auth_settings: + auth_setting = self.configuration.auth_settings().get(auth) + if auth_setting: + self._apply_auth_params( + headers, queries, resource_path, method, body, auth_setting + ) + + def _apply_auth_params( + self, headers, queries, resource_path, method, body, auth_setting + ): + if auth_setting["in"] == "cookie": + headers["Cookie"] = auth_setting["key"] + "=" + auth_setting["value"] + elif auth_setting["in"] == "header": + if auth_setting["type"] != "http-signature": + headers[auth_setting["key"]] = auth_setting["value"] + elif auth_setting["in"] == "query": + queries.append((auth_setting["key"], auth_setting["value"])) + else: + raise ApiValueError("Authentication token must be in `query` or `header`") + + +class Endpoint(object): + def __init__( + self, + settings=None, + params_map=None, + root_map=None, + headers_map=None, + api_client=None, + callable=None, + ): + """Creates an endpoint + + Args: + settings (dict): see below key value pairs + 'response_type' (tuple/None): response type + 'auth' (list): a list of auth type keys + 'endpoint_path' (str): the endpoint path + 'operation_id' (str): endpoint string identifier + 'http_method' (str): POST/PUT/PATCH/GET etc + 'servers' (list): list of str servers that this endpoint is at + params_map (dict): see below key value pairs + 'all' (list): list of str endpoint parameter names + 'required' (list): list of required parameter names + 'nullable' (list): list of nullable parameter names + 'enum' (list): list of parameters with enum values + 'validation' (list): list of parameters with validations + root_map + 'validations' (dict): the dict mapping endpoint parameter tuple + paths to their validation dictionaries + 'allowed_values' (dict): the dict mapping endpoint parameter + tuple paths to their allowed_values (enum) dictionaries + 'openapi_types' (dict): param_name to openapi type + 'attribute_map' (dict): param_name to camelCase name + 'location_map' (dict): param_name to 'body', 'file', 'form', + 'header', 'path', 'query' + collection_format_map (dict): param_name to `csv` etc. + headers_map (dict): see below key value pairs + 'accept' (list): list of Accept header strings + 'content_type' (list): list of Content-Type header strings + api_client (ApiClient) api client instance + callable (function): the function which is invoked when the + Endpoint is called + """ + self.settings = settings + self.params_map = params_map + self.params_map["all"].extend( + [ + "async_req", + "_host_index", + "_preload_content", + "_request_timeout", + "_return_http_data_only", + "_check_input_type", + "_check_return_type", + "_content_type", + "_spec_property_naming", + "_request_auths", + ] + ) + self.params_map["nullable"].extend(["_request_timeout"]) + self.validations = root_map["validations"] + self.allowed_values = root_map["allowed_values"] + self.openapi_types = root_map["openapi_types"] + extra_types = { + "async_req": (bool,), + "_host_index": (none_type, int), + "_preload_content": (bool,), + "_request_timeout": ( + none_type, + float, + (float,), + [float], + int, + (int,), + [int], + ), + "_return_http_data_only": (bool,), + "_check_input_type": (bool,), + "_check_return_type": (bool,), + "_spec_property_naming": (bool,), + "_content_type": (none_type, str), + "_request_auths": (none_type, list), + } + self.openapi_types.update(extra_types) + self.attribute_map = root_map["attribute_map"] + self.location_map = root_map["location_map"] + self.collection_format_map = root_map["collection_format_map"] + self.headers_map = headers_map + self.api_client = api_client + self.callable = callable + + def __validate_inputs(self, kwargs): + for param in self.params_map["enum"]: + if param in kwargs: + check_allowed_values(self.allowed_values, (param,), kwargs[param]) + + for param in self.params_map["validation"]: + if param in kwargs: + check_validations( + self.validations, + (param,), + kwargs[param], + configuration=self.api_client.configuration, + ) + + if kwargs["_check_input_type"] is False: + return + + for key, value in kwargs.items(): + fixed_val = validate_and_convert_types( + value, + self.openapi_types[key], + [key], + kwargs["_spec_property_naming"], + kwargs["_check_input_type"], + configuration=self.api_client.configuration, + ) + kwargs[key] = fixed_val + + def __gather_params(self, kwargs): + params = { + "body": None, + "collection_format": {}, + "file": {}, + "form": [], + "header": {}, + "path": {}, + "query": [], + } + + for param_name, param_value in kwargs.items(): + param_location = self.location_map.get(param_name) + if param_location is None: + continue + if param_location: + if param_location == "body": + params["body"] = param_value + continue + base_name = self.attribute_map[param_name] + if param_location == "form" and self.openapi_types[param_name] == ( + file_type, + ): + params["file"][base_name] = [param_value] + elif param_location == "form" and self.openapi_types[param_name] == ( + [file_type], + ): + # param_value is already a list + params["file"][base_name] = param_value + elif param_location in {"form", "query"}: + param_value_full = (base_name, param_value) + params[param_location].append(param_value_full) + if param_location not in {"form", "query"}: + params[param_location][base_name] = param_value + collection_format = self.collection_format_map.get(param_name) + if collection_format: + params["collection_format"][base_name] = collection_format + + return params + + def __call__(self, *args, **kwargs): + """This method is invoked when endpoints are called + Example: + + api_instance = DefaultApi() + api_instance.bt_to_csv_get # this is an instance of the class Endpoint + api_instance.bt_to_csv_get() # this invokes api_instance.bt_to_csv_get.__call__() + which then invokes the callable functions stored in that endpoint at + api_instance.bt_to_csv_get.callable or self.callable in this class + + """ + return self.callable(self, *args, **kwargs) + + def call_with_http_info(self, **kwargs): + + try: + index = ( + self.api_client.configuration.server_operation_index.get( + self.settings["operation_id"], + self.api_client.configuration.server_index, + ) + if kwargs["_host_index"] is None + else kwargs["_host_index"] + ) + server_variables = ( + self.api_client.configuration.server_operation_variables.get( + self.settings["operation_id"], + self.api_client.configuration.server_variables, + ) + ) + _host = self.api_client.configuration.get_host_from_settings( + index, variables=server_variables, servers=self.settings["servers"] + ) + except IndexError: + if self.settings["servers"]: + raise ApiValueError( + "Invalid host index. Must be 0 <= index < %s" + % len(self.settings["servers"]) + ) + _host = None + + for key, value in kwargs.items(): + if key not in self.params_map["all"]: + raise ApiTypeError( + "Got an unexpected parameter '%s'" + " to method `%s`" % (key, self.settings["operation_id"]) + ) + # only throw this nullable ApiValueError if _check_input_type + # is False, if _check_input_type==True we catch this case + # in self.__validate_inputs + if ( + key not in self.params_map["nullable"] + and value is None + and kwargs["_check_input_type"] is False + ): + raise ApiValueError( + "Value may not be None for non-nullable parameter `%s`" + " when calling `%s`" % (key, self.settings["operation_id"]) + ) + + for key in self.params_map["required"]: + if key not in kwargs.keys(): + raise ApiValueError( + "Missing the required parameter `%s` when calling " + "`%s`" % (key, self.settings["operation_id"]) + ) + + self.__validate_inputs(kwargs) + + params = self.__gather_params(kwargs) + + accept_headers_list = self.headers_map["accept"] + if accept_headers_list: + params["header"]["Accept"] = self.api_client.select_header_accept( + accept_headers_list + ) + + if kwargs.get("_content_type"): + params["header"]["Content-Type"] = kwargs["_content_type"] + else: + content_type_headers_list = self.headers_map["content_type"] + if content_type_headers_list: + if params["body"] != "": + content_types_list = self.api_client.select_header_content_type( + content_type_headers_list, + self.settings["http_method"], + params["body"], + ) + if content_types_list: + params["header"]["Content-Type"] = content_types_list + + return self.api_client.call_api( + self.settings["endpoint_path"], + self.settings["http_method"], + params["path"], + params["query"], + params["header"], + body=params["body"], + post_params=params["form"], + files=params["file"], + response_type=self.settings["response_type"], + auth_settings=self.settings["auth"], + async_req=kwargs["async_req"], + _check_type=kwargs["_check_return_type"], + _return_http_data_only=kwargs["_return_http_data_only"], + _preload_content=kwargs["_preload_content"], + _request_timeout=kwargs["_request_timeout"], + _host=_host, + _request_auths=kwargs["_request_auths"], + collection_formats=params["collection_format"], + ) diff --git a/python-client/deutschland/bundestag_tagesordnung/apis/__init__.py b/python-client/deutschland/bundestag_tagesordnung/apis/__init__.py new file mode 100644 index 0000000..361c4f1 --- /dev/null +++ b/python-client/deutschland/bundestag_tagesordnung/apis/__init__.py @@ -0,0 +1,16 @@ +# flake8: noqa + +# Import all APIs into this package. +# If you have many APIs here with many many models used in each API this may +# raise a `RecursionError`. +# In order to avoid this, import only the API that you directly need like: +# +# from deutschland.bundestag_tagesordnung.api.default_api import DefaultApi +# +# or import this package, but before doing it, use: +# +# import sys +# sys.setrecursionlimit(n) + +# Import APIs into API package: +from deutschland.bundestag_tagesordnung.api.default_api import DefaultApi diff --git a/python-client/deutschland/bundestag_tagesordnung/configuration.py b/python-client/deutschland/bundestag_tagesordnung/configuration.py new file mode 100644 index 0000000..cff593e --- /dev/null +++ b/python-client/deutschland/bundestag_tagesordnung/configuration.py @@ -0,0 +1,462 @@ +""" + Tagesordnungen API + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Contact: kontakt@bund.dev + Generated by: https://openapi-generator.tech +""" + +import copy +import logging +import multiprocessing +import sys +from http import client as http_client + +import urllib3 +from deutschland.bundestag_tagesordnung.exceptions import ApiValueError + +JSON_SCHEMA_VALIDATION_KEYWORDS = { + "multipleOf", + "maximum", + "exclusiveMaximum", + "minimum", + "exclusiveMinimum", + "maxLength", + "minLength", + "pattern", + "maxItems", + "minItems", +} + + +class Configuration(object): + """NOTE: This class is auto generated by OpenAPI Generator + + Ref: https://openapi-generator.tech + Do not edit the class manually. + + :param host: Base url + :param api_key: Dict to store API key(s). + Each entry in the dict specifies an API key. + The dict key is the name of the security scheme in the OAS specification. + The dict value is the API key secret. + :param api_key_prefix: Dict to store API prefix (e.g. Bearer) + The dict key is the name of the security scheme in the OAS specification. + The dict value is an API key prefix when generating the auth data. + :param username: Username for HTTP basic authentication + :param password: Password for HTTP basic authentication + :param discard_unknown_keys: Boolean value indicating whether to discard + unknown properties. A server may send a response that includes additional + properties that are not known by the client in the following scenarios: + 1. The OpenAPI document is incomplete, i.e. it does not match the server + implementation. + 2. The client was generated using an older version of the OpenAPI document + and the server has been upgraded since then. + If a schema in the OpenAPI document defines the additionalProperties attribute, + then all undeclared properties received by the server are injected into the + additional properties map. In that case, there are undeclared properties, and + nothing to discard. + :param disabled_client_side_validations (string): Comma-separated list of + JSON schema validation keywords to disable JSON schema structural validation + rules. The following keywords may be specified: multipleOf, maximum, + exclusiveMaximum, minimum, exclusiveMinimum, maxLength, minLength, pattern, + maxItems, minItems. + By default, the validation is performed for data generated locally by the client + and data received from the server, independent of any validation performed by + the server side. If the input data does not satisfy the JSON schema validation + rules specified in the OpenAPI document, an exception is raised. + If disabled_client_side_validations is set, structural validation is + disabled. This can be useful to troubleshoot data validation problem, such as + when the OpenAPI document validation rules do not match the actual API data + received by the server. + :param server_index: Index to servers configuration. + :param server_variables: Mapping with string values to replace variables in + templated server configuration. The validation of enums is performed for + variables with defined enum values before. + :param server_operation_index: Mapping from operation ID to an index to server + configuration. + :param server_operation_variables: Mapping from operation ID to a mapping with + string values to replace variables in templated server configuration. + The validation of enums is performed for variables with defined enum values before. + :param ssl_ca_cert: str - the path to a file of concatenated CA certificates + in PEM format + + """ + + _default = None + + def __init__( + self, + host=None, + api_key=None, + api_key_prefix=None, + access_token=None, + username=None, + password=None, + discard_unknown_keys=False, + disabled_client_side_validations="", + server_index=None, + server_variables=None, + server_operation_index=None, + server_operation_variables=None, + ssl_ca_cert=None, + ): + """Constructor""" + self._base_path = "https://api.hutt.io" if host is None else host + """Default Base url + """ + self.server_index = 0 if server_index is None and host is None else server_index + self.server_operation_index = server_operation_index or {} + """Default server index + """ + self.server_variables = server_variables or {} + self.server_operation_variables = server_operation_variables or {} + """Default server variables + """ + self.temp_folder_path = None + """Temp file folder for downloading files + """ + # Authentication Settings + self.access_token = access_token + self.api_key = {} + if api_key: + self.api_key = api_key + """dict to store API key(s) + """ + self.api_key_prefix = {} + if api_key_prefix: + self.api_key_prefix = api_key_prefix + """dict to store API prefix (e.g. Bearer) + """ + self.refresh_api_key_hook = None + """function hook to refresh API key if expired + """ + self.username = username + """Username for HTTP basic authentication + """ + self.password = password + """Password for HTTP basic authentication + """ + self.discard_unknown_keys = discard_unknown_keys + self.disabled_client_side_validations = disabled_client_side_validations + self.logger = {} + """Logging Settings + """ + self.logger["package_logger"] = logging.getLogger("bundestag_tagesordnung") + self.logger["urllib3_logger"] = logging.getLogger("urllib3") + self.logger_format = "%(asctime)s %(levelname)s %(message)s" + """Log format + """ + self.logger_stream_handler = None + """Log stream handler + """ + self.logger_file_handler = None + """Log file handler + """ + self.logger_file = None + """Debug file location + """ + self.debug = False + """Debug switch + """ + + self.verify_ssl = True + """SSL/TLS verification + Set this to false to skip verifying SSL certificate when calling API + from https server. + """ + self.ssl_ca_cert = ssl_ca_cert + """Set this to customize the certificate file to verify the peer. + """ + self.cert_file = None + """client certificate file + """ + self.key_file = None + """client key file + """ + self.assert_hostname = None + """Set this to True/False to enable/disable SSL hostname verification. + """ + + self.connection_pool_maxsize = multiprocessing.cpu_count() * 5 + """urllib3 connection pool's maximum number of connections saved + per pool. urllib3 uses 1 connection as default value, but this is + not the best value when you are making a lot of possibly parallel + requests to the same host, which is often the case here. + cpu_count * 5 is used as default value to increase performance. + """ + + self.proxy = None + """Proxy URL + """ + self.no_proxy = None + """bypass proxy for host in the no_proxy list. + """ + self.proxy_headers = None + """Proxy headers + """ + self.safe_chars_for_path_param = "" + """Safe chars for path_param + """ + self.retries = None + """Adding retries to override urllib3 default value 3 + """ + # Enable client side validation + self.client_side_validation = True + + # Options to pass down to the underlying urllib3 socket + self.socket_options = None + + def __deepcopy__(self, memo): + cls = self.__class__ + result = cls.__new__(cls) + memo[id(self)] = result + for k, v in self.__dict__.items(): + if k not in ("logger", "logger_file_handler"): + setattr(result, k, copy.deepcopy(v, memo)) + # shallow copy of loggers + result.logger = copy.copy(self.logger) + # use setters to configure loggers + result.logger_file = self.logger_file + result.debug = self.debug + return result + + def __setattr__(self, name, value): + object.__setattr__(self, name, value) + if name == "disabled_client_side_validations": + s = set(filter(None, value.split(","))) + for v in s: + if v not in JSON_SCHEMA_VALIDATION_KEYWORDS: + raise ApiValueError("Invalid keyword: '{0}''".format(v)) + self._disabled_client_side_validations = s + + @classmethod + def set_default(cls, default): + """Set default instance of configuration. + + It stores default configuration, which can be + returned by get_default_copy method. + + :param default: object of Configuration + """ + cls._default = copy.deepcopy(default) + + @classmethod + def get_default_copy(cls): + """Return new instance of configuration. + + This method returns newly created, based on default constructor, + object of Configuration class or returns a copy of default + configuration passed by the set_default method. + + :return: The configuration object. + """ + if cls._default is not None: + return copy.deepcopy(cls._default) + return Configuration() + + @property + def logger_file(self): + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + return self.__logger_file + + @logger_file.setter + def logger_file(self, value): + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + self.__logger_file = value + if self.__logger_file: + # If set logging file, + # then add file handler and remove stream handler. + self.logger_file_handler = logging.FileHandler(self.__logger_file) + self.logger_file_handler.setFormatter(self.logger_formatter) + for _, logger in self.logger.items(): + logger.addHandler(self.logger_file_handler) + + @property + def debug(self): + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + return self.__debug + + @debug.setter + def debug(self, value): + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + self.__debug = value + if self.__debug: + # if debug status is True, turn on debug logging + for _, logger in self.logger.items(): + logger.setLevel(logging.DEBUG) + # turn on http_client debug + http_client.HTTPConnection.debuglevel = 1 + else: + # if debug status is False, turn off debug logging, + # setting log level to default `logging.WARNING` + for _, logger in self.logger.items(): + logger.setLevel(logging.WARNING) + # turn off http_client debug + http_client.HTTPConnection.debuglevel = 0 + + @property + def logger_format(self): + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + return self.__logger_format + + @logger_format.setter + def logger_format(self, value): + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + self.__logger_format = value + self.logger_formatter = logging.Formatter(self.__logger_format) + + def get_api_key_with_prefix(self, identifier, alias=None): + """Gets API key (with prefix if set). + + :param identifier: The identifier of apiKey. + :param alias: The alternative identifier of apiKey. + :return: The token for api key authentication. + """ + if self.refresh_api_key_hook is not None: + self.refresh_api_key_hook(self) + key = self.api_key.get( + identifier, self.api_key.get(alias) if alias is not None else None + ) + if key: + prefix = self.api_key_prefix.get(identifier) + if prefix: + return "%s %s" % (prefix, key) + else: + return key + + def get_basic_auth_token(self): + """Gets HTTP basic authentication header (string). + + :return: The token for basic HTTP authentication. + """ + username = "" + if self.username is not None: + username = self.username + password = "" + if self.password is not None: + password = self.password + return urllib3.util.make_headers(basic_auth=username + ":" + password).get( + "authorization" + ) + + def auth_settings(self): + """Gets Auth Settings dict for api client. + + :return: The Auth Settings information dict. + """ + auth = {} + return auth + + def to_debug_report(self): + """Gets the essential information for debugging. + + :return: The report for debugging. + """ + return ( + "Python SDK Debug Report:\n" + "OS: {env}\n" + "Python Version: {pyversion}\n" + "Version of the API: 1.0.0\n" + "SDK Package Version: 1.0.0".format(env=sys.platform, pyversion=sys.version) + ) + + def get_host_settings(self): + """Gets an array of host settings + + :return: An array of host settings + """ + return [ + { + "url": "https://api.hutt.io", + "description": "No description provided", + } + ] + + def get_host_from_settings(self, index, variables=None, servers=None): + """Gets host URL based on the index and variables + :param index: array index of the host settings + :param variables: hash of variable and the corresponding value + :param servers: an array of host settings or None + :return: URL based on host settings + """ + if index is None: + return self._base_path + + variables = {} if variables is None else variables + servers = self.get_host_settings() if servers is None else servers + + try: + server = servers[index] + except IndexError: + raise ValueError( + "Invalid index {0} when selecting the host settings. " + "Must be less than {1}".format(index, len(servers)) + ) + + url = server["url"] + + # go through variables and replace placeholders + for variable_name, variable in server.get("variables", {}).items(): + used_value = variables.get(variable_name, variable["default_value"]) + + if "enum_values" in variable and used_value not in variable["enum_values"]: + raise ValueError( + "The variable `{0}` in the host URL has invalid value " + "{1}. Must be {2}.".format( + variable_name, variables[variable_name], variable["enum_values"] + ) + ) + + url = url.replace("{" + variable_name + "}", used_value) + + return url + + @property + def host(self): + """Return generated host.""" + return self.get_host_from_settings( + self.server_index, variables=self.server_variables + ) + + @host.setter + def host(self, value): + """Fix base path.""" + self._base_path = value + self.server_index = None diff --git a/python-client/deutschland/bundestag_tagesordnung/exceptions.py b/python-client/deutschland/bundestag_tagesordnung/exceptions.py new file mode 100644 index 0000000..645a8ff --- /dev/null +++ b/python-client/deutschland/bundestag_tagesordnung/exceptions.py @@ -0,0 +1,158 @@ +""" + Tagesordnungen API + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Contact: kontakt@bund.dev + Generated by: https://openapi-generator.tech +""" + + +class OpenApiException(Exception): + """The base exception class for all OpenAPIExceptions""" + + +class ApiTypeError(OpenApiException, TypeError): + def __init__(self, msg, path_to_item=None, valid_classes=None, key_type=None): + """Raises an exception for TypeErrors + + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list): a list of keys an indices to get to the + current_item + None if unset + valid_classes (tuple): the primitive classes that current item + should be an instance of + None if unset + key_type (bool): False if our value is a value in a dict + True if it is a key in a dict + False if our item is an item in a list + None if unset + """ + self.path_to_item = path_to_item + self.valid_classes = valid_classes + self.key_type = key_type + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiTypeError, self).__init__(full_msg) + + +class ApiValueError(OpenApiException, ValueError): + def __init__(self, msg, path_to_item=None): + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list) the path to the exception in the + received_data dict. None if unset + """ + + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiValueError, self).__init__(full_msg) + + +class ApiAttributeError(OpenApiException, AttributeError): + def __init__(self, msg, path_to_item=None): + """ + Raised when an attribute reference or assignment fails. + + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (None/list) the path to the exception in the + received_data dict + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiAttributeError, self).__init__(full_msg) + + +class ApiKeyError(OpenApiException, KeyError): + def __init__(self, msg, path_to_item=None): + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (None/list) the path to the exception in the + received_data dict + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiKeyError, self).__init__(full_msg) + + +class ApiException(OpenApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + if http_resp: + self.status = http_resp.status + self.reason = http_resp.reason + self.body = http_resp.data + self.headers = http_resp.getheaders() + else: + self.status = status + self.reason = reason + self.body = None + self.headers = None + + def __str__(self): + """Custom error messages for exception""" + error_message = "Status Code: {0}\n" "Reason: {1}\n".format( + self.status, self.reason + ) + if self.headers: + error_message += "HTTP response headers: {0}\n".format(self.headers) + + if self.body: + error_message += "HTTP response body: {0}\n".format(self.body) + + return error_message + + +class NotFoundException(ApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + super(NotFoundException, self).__init__(status, reason, http_resp) + + +class UnauthorizedException(ApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + super(UnauthorizedException, self).__init__(status, reason, http_resp) + + +class ForbiddenException(ApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + super(ForbiddenException, self).__init__(status, reason, http_resp) + + +class ServiceException(ApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + super(ServiceException, self).__init__(status, reason, http_resp) + + +def render_path(path_to_item): + """Returns a string representation of a path""" + result = "" + for pth in path_to_item: + if isinstance(pth, int): + result += "[{0}]".format(pth) + else: + result += "['{0}']".format(pth) + return result diff --git a/python-client/deutschland/bundestag_tagesordnung/model/__init__.py b/python-client/deutschland/bundestag_tagesordnung/model/__init__.py new file mode 100644 index 0000000..e439869 --- /dev/null +++ b/python-client/deutschland/bundestag_tagesordnung/model/__init__.py @@ -0,0 +1,5 @@ +# we can not import model classes here because that would create a circular +# reference which would not work in python2 +# do not import all models into this module because that uses a lot of memory and stack frames +# if you need the ability to import all models from one package, import them with +# from deutschland.bundestag_tagesordnung.models import ModelA, ModelB diff --git a/python-client/deutschland/bundestag_tagesordnung/model_utils.py b/python-client/deutschland/bundestag_tagesordnung/model_utils.py new file mode 100644 index 0000000..96ff8b6 --- /dev/null +++ b/python-client/deutschland/bundestag_tagesordnung/model_utils.py @@ -0,0 +1,2090 @@ +""" + Tagesordnungen API + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Contact: kontakt@bund.dev + Generated by: https://openapi-generator.tech +""" + +import inspect +import io +import os +import pprint +import re +import tempfile +import uuid +from copy import deepcopy +from datetime import date, datetime # noqa: F401 + +from dateutil.parser import parse +from deutschland.bundestag_tagesordnung.exceptions import ( + ApiAttributeError, + ApiKeyError, + ApiTypeError, + ApiValueError, +) + +none_type = type(None) +file_type = io.IOBase + + +def convert_js_args_to_python_args(fn): + from functools import wraps + + @wraps(fn) + def wrapped_init(_self, *args, **kwargs): + """ + An attribute named `self` received from the api will conflicts with the reserved `self` + parameter of a class method. During generation, `self` attributes are mapped + to `_self` in models. Here, we name `_self` instead of `self` to avoid conflicts. + """ + spec_property_naming = kwargs.get("_spec_property_naming", False) + if spec_property_naming: + kwargs = change_keys_js_to_python( + kwargs, _self if isinstance(_self, type) else _self.__class__ + ) + return fn(_self, *args, **kwargs) + + return wrapped_init + + +class cached_property(object): + # this caches the result of the function call for fn with no inputs + # use this as a decorator on function methods that you want converted + # into cached properties + result_key = "_results" + + def __init__(self, fn): + self._fn = fn + + def __get__(self, instance, cls=None): + if self.result_key in vars(self): + return vars(self)[self.result_key] + else: + result = self._fn() + setattr(self, self.result_key, result) + return result + + +PRIMITIVE_TYPES = (list, float, int, bool, datetime, date, str, file_type) + + +def allows_single_value_input(cls): + """ + This function returns True if the input composed schema model or any + descendant model allows a value only input + This is true for cases where oneOf contains items like: + oneOf: + - float + - NumberWithValidation + - StringEnum + - ArrayModel + - null + TODO: lru_cache this + """ + if issubclass(cls, ModelSimple) or cls in PRIMITIVE_TYPES: + return True + elif issubclass(cls, ModelComposed): + if not cls._composed_schemas["oneOf"]: + return False + return any(allows_single_value_input(c) for c in cls._composed_schemas["oneOf"]) + return False + + +def composed_model_input_classes(cls): + """ + This function returns a list of the possible models that can be accepted as + inputs. + TODO: lru_cache this + """ + if issubclass(cls, ModelSimple) or cls in PRIMITIVE_TYPES: + return [cls] + elif issubclass(cls, ModelNormal): + if cls.discriminator is None: + return [cls] + else: + return get_discriminated_classes(cls) + elif issubclass(cls, ModelComposed): + if not cls._composed_schemas["oneOf"]: + return [] + if cls.discriminator is None: + input_classes = [] + for c in cls._composed_schemas["oneOf"]: + input_classes.extend(composed_model_input_classes(c)) + return input_classes + else: + return get_discriminated_classes(cls) + return [] + + +class OpenApiModel(object): + """The base class for all OpenAPIModels""" + + def set_attribute(self, name, value): + # this is only used to set properties on self + + path_to_item = [] + if self._path_to_item: + path_to_item.extend(self._path_to_item) + path_to_item.append(name) + + if name in self.openapi_types: + required_types_mixed = self.openapi_types[name] + elif self.additional_properties_type is None: + raise ApiAttributeError( + "{0} has no attribute '{1}'".format(type(self).__name__, name), + path_to_item, + ) + elif self.additional_properties_type is not None: + required_types_mixed = self.additional_properties_type + + if get_simple_class(name) != str: + error_msg = type_error_message( + var_name=name, var_value=name, valid_classes=(str,), key_type=True + ) + raise ApiTypeError( + error_msg, + path_to_item=path_to_item, + valid_classes=(str,), + key_type=True, + ) + + if self._check_type: + value = validate_and_convert_types( + value, + required_types_mixed, + path_to_item, + self._spec_property_naming, + self._check_type, + configuration=self._configuration, + ) + if (name,) in self.allowed_values: + check_allowed_values(self.allowed_values, (name,), value) + if (name,) in self.validations: + check_validations(self.validations, (name,), value, self._configuration) + self.__dict__["_data_store"][name] = value + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other + + def __setattr__(self, attr, value): + """set the value of an attribute using dot notation: `instance.attr = val`""" + self[attr] = value + + def __getattr__(self, attr): + """get the value of an attribute using dot notation: `instance.attr`""" + return self.__getitem__(attr) + + def __copy__(self): + cls = self.__class__ + if self.get("_spec_property_naming", False): + return cls._new_from_openapi_data(**self.__dict__) + else: + return cls.__new__(cls, **self.__dict__) + + def __deepcopy__(self, memo): + cls = self.__class__ + + if self.get("_spec_property_naming", False): + new_inst = cls._new_from_openapi_data() + else: + new_inst = cls.__new__(cls, **self.__dict__) + + for k, v in self.__dict__.items(): + setattr(new_inst, k, deepcopy(v, memo)) + return new_inst + + def __new__(cls, *args, **kwargs): + # this function uses the discriminator to + # pick a new schema/class to instantiate because a discriminator + # propertyName value was passed in + + if len(args) == 1: + arg = args[0] + if arg is None and is_type_nullable(cls): + # The input data is the 'null' value and the type is nullable. + return None + + if issubclass(cls, ModelComposed) and allows_single_value_input(cls): + model_kwargs = {} + oneof_instance = get_oneof_instance( + cls, model_kwargs, kwargs, model_arg=arg + ) + return oneof_instance + + visited_composed_classes = kwargs.get("_visited_composed_classes", ()) + if cls.discriminator is None or cls in visited_composed_classes: + # Use case 1: this openapi schema (cls) does not have a discriminator + # Use case 2: we have already visited this class before and are sure that we + # want to instantiate it this time. We have visited this class deserializing + # a payload with a discriminator. During that process we traveled through + # this class but did not make an instance of it. Now we are making an + # instance of a composed class which contains cls in it, so this time make an instance of cls. + # + # Here's an example of use case 2: If Animal has a discriminator + # petType and we pass in "Dog", and the class Dog + # allOf includes Animal, we move through Animal + # once using the discriminator, and pick Dog. + # Then in the composed schema dog Dog, we will make an instance of the + # Animal class (because Dal has allOf: Animal) but this time we won't travel + # through Animal's discriminator because we passed in + # _visited_composed_classes = (Animal,) + + return super(OpenApiModel, cls).__new__(cls) + + # Get the name and value of the discriminator property. + # The discriminator name is obtained from the discriminator meta-data + # and the discriminator value is obtained from the input data. + discr_propertyname_py = list(cls.discriminator.keys())[0] + discr_propertyname_js = cls.attribute_map[discr_propertyname_py] + if discr_propertyname_js in kwargs: + discr_value = kwargs[discr_propertyname_js] + elif discr_propertyname_py in kwargs: + discr_value = kwargs[discr_propertyname_py] + else: + # The input data does not contain the discriminator property. + path_to_item = kwargs.get("_path_to_item", ()) + raise ApiValueError( + "Cannot deserialize input data due to missing discriminator. " + "The discriminator property '%s' is missing at path: %s" + % (discr_propertyname_js, path_to_item) + ) + + # Implementation note: the last argument to get_discriminator_class + # is a list of visited classes. get_discriminator_class may recursively + # call itself and update the list of visited classes, and the initial + # value must be an empty list. Hence not using 'visited_composed_classes' + new_cls = get_discriminator_class(cls, discr_propertyname_py, discr_value, []) + if new_cls is None: + path_to_item = kwargs.get("_path_to_item", ()) + disc_prop_value = kwargs.get( + discr_propertyname_js, kwargs.get(discr_propertyname_py) + ) + raise ApiValueError( + "Cannot deserialize input data due to invalid discriminator " + "value. The OpenAPI document has no mapping for discriminator " + "property '%s'='%s' at path: %s" + % (discr_propertyname_js, disc_prop_value, path_to_item) + ) + + if new_cls in visited_composed_classes: + # if we are making an instance of a composed schema Descendent + # which allOf includes Ancestor, then Ancestor contains + # a discriminator that includes Descendent. + # So if we make an instance of Descendent, we have to make an + # instance of Ancestor to hold the allOf properties. + # This code detects that use case and makes the instance of Ancestor + # For example: + # When making an instance of Dog, _visited_composed_classes = (Dog,) + # then we make an instance of Animal to include in dog._composed_instances + # so when we are here, cls is Animal + # cls.discriminator != None + # cls not in _visited_composed_classes + # new_cls = Dog + # but we know we know that we already have Dog + # because it is in visited_composed_classes + # so make Animal here + return super(OpenApiModel, cls).__new__(cls) + + # Build a list containing all oneOf and anyOf descendants. + oneof_anyof_classes = None + if cls._composed_schemas is not None: + oneof_anyof_classes = cls._composed_schemas.get( + "oneOf", () + ) + cls._composed_schemas.get("anyOf", ()) + oneof_anyof_child = new_cls in oneof_anyof_classes + kwargs["_visited_composed_classes"] = visited_composed_classes + (cls,) + + if cls._composed_schemas.get("allOf") and oneof_anyof_child: + # Validate that we can make self because when we make the + # new_cls it will not include the allOf validations in self + self_inst = super(OpenApiModel, cls).__new__(cls) + self_inst.__init__(*args, **kwargs) + + if kwargs.get("_spec_property_naming", False): + # when true, implies new is from deserialization + new_inst = new_cls._new_from_openapi_data(*args, **kwargs) + else: + new_inst = new_cls.__new__(new_cls, *args, **kwargs) + new_inst.__init__(*args, **kwargs) + + return new_inst + + @classmethod + @convert_js_args_to_python_args + def _new_from_openapi_data(cls, *args, **kwargs): + # this function uses the discriminator to + # pick a new schema/class to instantiate because a discriminator + # propertyName value was passed in + + if len(args) == 1: + arg = args[0] + if arg is None and is_type_nullable(cls): + # The input data is the 'null' value and the type is nullable. + return None + + if issubclass(cls, ModelComposed) and allows_single_value_input(cls): + model_kwargs = {} + oneof_instance = get_oneof_instance( + cls, model_kwargs, kwargs, model_arg=arg + ) + return oneof_instance + + visited_composed_classes = kwargs.get("_visited_composed_classes", ()) + if cls.discriminator is None or cls in visited_composed_classes: + # Use case 1: this openapi schema (cls) does not have a discriminator + # Use case 2: we have already visited this class before and are sure that we + # want to instantiate it this time. We have visited this class deserializing + # a payload with a discriminator. During that process we traveled through + # this class but did not make an instance of it. Now we are making an + # instance of a composed class which contains cls in it, so this time make an instance of cls. + # + # Here's an example of use case 2: If Animal has a discriminator + # petType and we pass in "Dog", and the class Dog + # allOf includes Animal, we move through Animal + # once using the discriminator, and pick Dog. + # Then in the composed schema dog Dog, we will make an instance of the + # Animal class (because Dal has allOf: Animal) but this time we won't travel + # through Animal's discriminator because we passed in + # _visited_composed_classes = (Animal,) + + return cls._from_openapi_data(*args, **kwargs) + + # Get the name and value of the discriminator property. + # The discriminator name is obtained from the discriminator meta-data + # and the discriminator value is obtained from the input data. + discr_propertyname_py = list(cls.discriminator.keys())[0] + discr_propertyname_js = cls.attribute_map[discr_propertyname_py] + if discr_propertyname_js in kwargs: + discr_value = kwargs[discr_propertyname_js] + elif discr_propertyname_py in kwargs: + discr_value = kwargs[discr_propertyname_py] + else: + # The input data does not contain the discriminator property. + path_to_item = kwargs.get("_path_to_item", ()) + raise ApiValueError( + "Cannot deserialize input data due to missing discriminator. " + "The discriminator property '%s' is missing at path: %s" + % (discr_propertyname_js, path_to_item) + ) + + # Implementation note: the last argument to get_discriminator_class + # is a list of visited classes. get_discriminator_class may recursively + # call itself and update the list of visited classes, and the initial + # value must be an empty list. Hence not using 'visited_composed_classes' + new_cls = get_discriminator_class(cls, discr_propertyname_py, discr_value, []) + if new_cls is None: + path_to_item = kwargs.get("_path_to_item", ()) + disc_prop_value = kwargs.get( + discr_propertyname_js, kwargs.get(discr_propertyname_py) + ) + raise ApiValueError( + "Cannot deserialize input data due to invalid discriminator " + "value. The OpenAPI document has no mapping for discriminator " + "property '%s'='%s' at path: %s" + % (discr_propertyname_js, disc_prop_value, path_to_item) + ) + + if new_cls in visited_composed_classes: + # if we are making an instance of a composed schema Descendent + # which allOf includes Ancestor, then Ancestor contains + # a discriminator that includes Descendent. + # So if we make an instance of Descendent, we have to make an + # instance of Ancestor to hold the allOf properties. + # This code detects that use case and makes the instance of Ancestor + # For example: + # When making an instance of Dog, _visited_composed_classes = (Dog,) + # then we make an instance of Animal to include in dog._composed_instances + # so when we are here, cls is Animal + # cls.discriminator != None + # cls not in _visited_composed_classes + # new_cls = Dog + # but we know we know that we already have Dog + # because it is in visited_composed_classes + # so make Animal here + return cls._from_openapi_data(*args, **kwargs) + + # Build a list containing all oneOf and anyOf descendants. + oneof_anyof_classes = None + if cls._composed_schemas is not None: + oneof_anyof_classes = cls._composed_schemas.get( + "oneOf", () + ) + cls._composed_schemas.get("anyOf", ()) + oneof_anyof_child = new_cls in oneof_anyof_classes + kwargs["_visited_composed_classes"] = visited_composed_classes + (cls,) + + if cls._composed_schemas.get("allOf") and oneof_anyof_child: + # Validate that we can make self because when we make the + # new_cls it will not include the allOf validations in self + self_inst = cls._from_openapi_data(*args, **kwargs) + + new_inst = new_cls._new_from_openapi_data(*args, **kwargs) + return new_inst + + +class ModelSimple(OpenApiModel): + """the parent class of models whose type != object in their + swagger/openapi""" + + def __setitem__(self, name, value): + """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" + if name in self.required_properties: + self.__dict__[name] = value + return + + self.set_attribute(name, value) + + def get(self, name, default=None): + """returns the value of an attribute or some default value if the attribute was not set""" + if name in self.required_properties: + return self.__dict__[name] + + return self.__dict__["_data_store"].get(name, default) + + def __getitem__(self, name): + """get the value of an attribute using square-bracket notation: `instance[attr]`""" + if name in self: + return self.get(name) + + raise ApiAttributeError( + "{0} has no attribute '{1}'".format(type(self).__name__, name), + [e for e in [self._path_to_item, name] if e], + ) + + def __contains__(self, name): + """used by `in` operator to check if an attribute value was set in an instance: `'attr' in instance`""" + if name in self.required_properties: + return name in self.__dict__ + + return name in self.__dict__["_data_store"] + + def to_str(self): + """Returns the string representation of the model""" + return str(self.value) + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, self.__class__): + return False + + this_val = self._data_store["value"] + that_val = other._data_store["value"] + types = set() + types.add(this_val.__class__) + types.add(that_val.__class__) + vals_equal = this_val == that_val + return vals_equal + + +class ModelNormal(OpenApiModel): + """the parent class of models whose type == object in their + swagger/openapi""" + + def __setitem__(self, name, value): + """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" + if name in self.required_properties: + self.__dict__[name] = value + return + + self.set_attribute(name, value) + + def get(self, name, default=None): + """returns the value of an attribute or some default value if the attribute was not set""" + if name in self.required_properties: + return self.__dict__[name] + + return self.__dict__["_data_store"].get(name, default) + + def __getitem__(self, name): + """get the value of an attribute using square-bracket notation: `instance[attr]`""" + if name in self: + return self.get(name) + + raise ApiAttributeError( + "{0} has no attribute '{1}'".format(type(self).__name__, name), + [e for e in [self._path_to_item, name] if e], + ) + + def __contains__(self, name): + """used by `in` operator to check if an attribute value was set in an instance: `'attr' in instance`""" + if name in self.required_properties: + return name in self.__dict__ + + return name in self.__dict__["_data_store"] + + def to_dict(self): + """Returns the model properties as a dict""" + return model_to_dict(self, serialize=False) + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, self.__class__): + return False + + if not set(self._data_store.keys()) == set(other._data_store.keys()): + return False + for _var_name, this_val in self._data_store.items(): + that_val = other._data_store[_var_name] + types = set() + types.add(this_val.__class__) + types.add(that_val.__class__) + vals_equal = this_val == that_val + if not vals_equal: + return False + return True + + +class ModelComposed(OpenApiModel): + """the parent class of models whose type == object in their + swagger/openapi and have oneOf/allOf/anyOf + + When one sets a property we use var_name_to_model_instances to store the value in + the correct class instances + run any type checking + validation code. + When one gets a property we use var_name_to_model_instances to get the value + from the correct class instances. + This allows multiple composed schemas to contain the same property with additive + constraints on the value. + + _composed_schemas (dict) stores the anyOf/allOf/oneOf classes + key (str): allOf/oneOf/anyOf + value (list): the classes in the XOf definition. + Note: none_type can be included when the openapi document version >= 3.1.0 + _composed_instances (list): stores a list of instances of the composed schemas + defined in _composed_schemas. When properties are accessed in the self instance, + they are returned from the self._data_store or the data stores in the instances + in self._composed_schemas + _var_name_to_model_instances (dict): maps between a variable name on self and + the composed instances (self included) which contain that data + key (str): property name + value (list): list of class instances, self or instances in _composed_instances + which contain the value that the key is referring to. + """ + + def __setitem__(self, name, value): + """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" + if name in self.required_properties: + self.__dict__[name] = value + return + + """ + Use cases: + 1. additional_properties_type is None (additionalProperties == False in spec) + Check for property presence in self.openapi_types + if not present then throw an error + if present set in self, set attribute + always set on composed schemas + 2. additional_properties_type exists + set attribute on self + always set on composed schemas + """ + if self.additional_properties_type is None: + """ + For an attribute to exist on a composed schema it must: + - fulfill schema_requirements in the self composed schema not considering oneOf/anyOf/allOf schemas AND + - fulfill schema_requirements in each oneOf/anyOf/allOf schemas + + schema_requirements: + For an attribute to exist on a schema it must: + - be present in properties at the schema OR + - have additionalProperties unset (defaults additionalProperties = any type) OR + - have additionalProperties set + """ + if name not in self.openapi_types: + raise ApiAttributeError( + "{0} has no attribute '{1}'".format(type(self).__name__, name), + [e for e in [self._path_to_item, name] if e], + ) + # attribute must be set on self and composed instances + self.set_attribute(name, value) + for model_instance in self._composed_instances: + setattr(model_instance, name, value) + if name not in self._var_name_to_model_instances: + # we assigned an additional property + self.__dict__["_var_name_to_model_instances"][name] = ( + self._composed_instances + [self] + ) + return None + + __unset_attribute_value__ = object() + + def get(self, name, default=None): + """returns the value of an attribute or some default value if the attribute was not set""" + if name in self.required_properties: + return self.__dict__[name] + + # get the attribute from the correct instance + model_instances = self._var_name_to_model_instances.get(name) + values = [] + # A composed model stores self and child (oneof/anyOf/allOf) models under + # self._var_name_to_model_instances. + # Any property must exist in self and all model instances + # The value stored in all model instances must be the same + if model_instances: + for model_instance in model_instances: + if name in model_instance._data_store: + v = model_instance._data_store[name] + if v not in values: + values.append(v) + len_values = len(values) + if len_values == 0: + return default + elif len_values == 1: + return values[0] + elif len_values > 1: + raise ApiValueError( + "Values stored for property {0} in {1} differ when looking " + "at self and self's composed instances. All values must be " + "the same".format(name, type(self).__name__), + [e for e in [self._path_to_item, name] if e], + ) + + def __getitem__(self, name): + """get the value of an attribute using square-bracket notation: `instance[attr]`""" + value = self.get(name, self.__unset_attribute_value__) + if value is self.__unset_attribute_value__: + raise ApiAttributeError( + "{0} has no attribute '{1}'".format(type(self).__name__, name), + [e for e in [self._path_to_item, name] if e], + ) + return value + + def __contains__(self, name): + """used by `in` operator to check if an attribute value was set in an instance: `'attr' in instance`""" + + if name in self.required_properties: + return name in self.__dict__ + + model_instances = self._var_name_to_model_instances.get( + name, self._additional_properties_model_instances + ) + + if model_instances: + for model_instance in model_instances: + if name in model_instance._data_store: + return True + + return False + + def to_dict(self): + """Returns the model properties as a dict""" + return model_to_dict(self, serialize=False) + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, self.__class__): + return False + + if not set(self._data_store.keys()) == set(other._data_store.keys()): + return False + for _var_name, this_val in self._data_store.items(): + that_val = other._data_store[_var_name] + types = set() + types.add(this_val.__class__) + types.add(that_val.__class__) + vals_equal = this_val == that_val + if not vals_equal: + return False + return True + + +COERCION_INDEX_BY_TYPE = { + ModelComposed: 0, + ModelNormal: 1, + ModelSimple: 2, + none_type: 3, # The type of 'None'. + list: 4, + dict: 5, + float: 6, + int: 7, + bool: 8, + datetime: 9, + date: 10, + str: 11, + file_type: 12, # 'file_type' is an alias for the built-in 'file' or 'io.IOBase' type. +} + +# these are used to limit what type conversions we try to do +# when we have a valid type already and we want to try converting +# to another type +UPCONVERSION_TYPE_PAIRS = ( + (str, datetime), + (str, date), + # A float may be serialized as an integer, e.g. '3' is a valid serialized float. + (int, float), + (list, ModelComposed), + (dict, ModelComposed), + (str, ModelComposed), + (int, ModelComposed), + (float, ModelComposed), + (list, ModelComposed), + (list, ModelNormal), + (dict, ModelNormal), + (str, ModelSimple), + (int, ModelSimple), + (float, ModelSimple), + (list, ModelSimple), +) + +COERCIBLE_TYPE_PAIRS = { + False: ( # client instantiation of a model with client data + # (dict, ModelComposed), + # (list, ModelComposed), + # (dict, ModelNormal), + # (list, ModelNormal), + # (str, ModelSimple), + # (int, ModelSimple), + # (float, ModelSimple), + # (list, ModelSimple), + # (str, int), + # (str, float), + # (str, datetime), + # (str, date), + # (int, str), + # (float, str), + ), + True: ( # server -> client data + (dict, ModelComposed), + (list, ModelComposed), + (dict, ModelNormal), + (list, ModelNormal), + (str, ModelSimple), + (int, ModelSimple), + (float, ModelSimple), + (list, ModelSimple), + # (str, int), + # (str, float), + (str, datetime), + (str, date), + # (int, str), + # (float, str), + (str, file_type), + ), +} + + +def get_simple_class(input_value): + """Returns an input_value's simple class that we will use for type checking + Python2: + float and int will return int, where int is the python3 int backport + str and unicode will return str, where str is the python3 str backport + Note: float and int ARE both instances of int backport + Note: str_py2 and unicode_py2 are NOT both instances of str backport + + Args: + input_value (class/class_instance): the item for which we will return + the simple class + """ + if isinstance(input_value, type): + # input_value is a class + return input_value + elif isinstance(input_value, tuple): + return tuple + elif isinstance(input_value, list): + return list + elif isinstance(input_value, dict): + return dict + elif isinstance(input_value, none_type): + return none_type + elif isinstance(input_value, file_type): + return file_type + elif isinstance(input_value, bool): + # this must be higher than the int check because + # isinstance(True, int) == True + return bool + elif isinstance(input_value, int): + return int + elif isinstance(input_value, datetime): + # this must be higher than the date check because + # isinstance(datetime_instance, date) == True + return datetime + elif isinstance(input_value, date): + return date + elif isinstance(input_value, str): + return str + return type(input_value) + + +def check_allowed_values(allowed_values, input_variable_path, input_values): + """Raises an exception if the input_values are not allowed + + Args: + allowed_values (dict): the allowed_values dict + input_variable_path (tuple): the path to the input variable + input_values (list/str/int/float/date/datetime): the values that we + are checking to see if they are in allowed_values + """ + these_allowed_values = list(allowed_values[input_variable_path].values()) + if isinstance(input_values, list) and not set(input_values).issubset( + set(these_allowed_values) + ): + invalid_values = ( + ", ".join(map(str, set(input_values) - set(these_allowed_values))), + ) + raise ApiValueError( + "Invalid values for `%s` [%s], must be a subset of [%s]" + % ( + input_variable_path[0], + invalid_values, + ", ".join(map(str, these_allowed_values)), + ) + ) + elif isinstance(input_values, dict) and not set(input_values.keys()).issubset( + set(these_allowed_values) + ): + invalid_values = ", ".join( + map(str, set(input_values.keys()) - set(these_allowed_values)) + ) + raise ApiValueError( + "Invalid keys in `%s` [%s], must be a subset of [%s]" + % ( + input_variable_path[0], + invalid_values, + ", ".join(map(str, these_allowed_values)), + ) + ) + elif ( + not isinstance(input_values, (list, dict)) + and input_values not in these_allowed_values + ): + raise ApiValueError( + "Invalid value for `%s` (%s), must be one of %s" + % (input_variable_path[0], input_values, these_allowed_values) + ) + + +def is_json_validation_enabled(schema_keyword, configuration=None): + """Returns true if JSON schema validation is enabled for the specified + validation keyword. This can be used to skip JSON schema structural validation + as requested in the configuration. + + Args: + schema_keyword (string): the name of a JSON schema validation keyword. + configuration (Configuration): the configuration class. + """ + + return ( + configuration is None + or not hasattr(configuration, "_disabled_client_side_validations") + or schema_keyword not in configuration._disabled_client_side_validations + ) + + +def check_validations( + validations, input_variable_path, input_values, configuration=None +): + """Raises an exception if the input_values are invalid + + Args: + validations (dict): the validation dictionary. + input_variable_path (tuple): the path to the input variable. + input_values (list/str/int/float/date/datetime): the values that we + are checking. + configuration (Configuration): the configuration class. + """ + + if input_values is None: + return + + current_validations = validations[input_variable_path] + if ( + is_json_validation_enabled("multipleOf", configuration) + and "multiple_of" in current_validations + and isinstance(input_values, (int, float)) + and not (float(input_values) / current_validations["multiple_of"]).is_integer() + ): + # Note 'multipleOf' will be as good as the floating point arithmetic. + raise ApiValueError( + "Invalid value for `%s`, value must be a multiple of " + "`%s`" % (input_variable_path[0], current_validations["multiple_of"]) + ) + + if ( + is_json_validation_enabled("maxLength", configuration) + and "max_length" in current_validations + and len(input_values) > current_validations["max_length"] + ): + raise ApiValueError( + "Invalid value for `%s`, length must be less than or equal to " + "`%s`" % (input_variable_path[0], current_validations["max_length"]) + ) + + if ( + is_json_validation_enabled("minLength", configuration) + and "min_length" in current_validations + and len(input_values) < current_validations["min_length"] + ): + raise ApiValueError( + "Invalid value for `%s`, length must be greater than or equal to " + "`%s`" % (input_variable_path[0], current_validations["min_length"]) + ) + + if ( + is_json_validation_enabled("maxItems", configuration) + and "max_items" in current_validations + and len(input_values) > current_validations["max_items"] + ): + raise ApiValueError( + "Invalid value for `%s`, number of items must be less than or " + "equal to `%s`" % (input_variable_path[0], current_validations["max_items"]) + ) + + if ( + is_json_validation_enabled("minItems", configuration) + and "min_items" in current_validations + and len(input_values) < current_validations["min_items"] + ): + raise ValueError( + "Invalid value for `%s`, number of items must be greater than or " + "equal to `%s`" % (input_variable_path[0], current_validations["min_items"]) + ) + + items = ( + "exclusive_maximum", + "inclusive_maximum", + "exclusive_minimum", + "inclusive_minimum", + ) + if any(item in current_validations for item in items): + if isinstance(input_values, list): + max_val = max(input_values) + min_val = min(input_values) + elif isinstance(input_values, dict): + max_val = max(input_values.values()) + min_val = min(input_values.values()) + else: + max_val = input_values + min_val = input_values + + if ( + is_json_validation_enabled("exclusiveMaximum", configuration) + and "exclusive_maximum" in current_validations + and max_val >= current_validations["exclusive_maximum"] + ): + raise ApiValueError( + "Invalid value for `%s`, must be a value less than `%s`" + % (input_variable_path[0], current_validations["exclusive_maximum"]) + ) + + if ( + is_json_validation_enabled("maximum", configuration) + and "inclusive_maximum" in current_validations + and max_val > current_validations["inclusive_maximum"] + ): + raise ApiValueError( + "Invalid value for `%s`, must be a value less than or equal to " + "`%s`" % (input_variable_path[0], current_validations["inclusive_maximum"]) + ) + + if ( + is_json_validation_enabled("exclusiveMinimum", configuration) + and "exclusive_minimum" in current_validations + and min_val <= current_validations["exclusive_minimum"] + ): + raise ApiValueError( + "Invalid value for `%s`, must be a value greater than `%s`" + % (input_variable_path[0], current_validations["exclusive_maximum"]) + ) + + if ( + is_json_validation_enabled("minimum", configuration) + and "inclusive_minimum" in current_validations + and min_val < current_validations["inclusive_minimum"] + ): + raise ApiValueError( + "Invalid value for `%s`, must be a value greater than or equal " + "to `%s`" + % (input_variable_path[0], current_validations["inclusive_minimum"]) + ) + flags = current_validations.get("regex", {}).get("flags", 0) + if ( + is_json_validation_enabled("pattern", configuration) + and "regex" in current_validations + and not re.search( + current_validations["regex"]["pattern"], input_values, flags=flags + ) + ): + err_msg = r"Invalid value for `%s`, must match regular expression `%s`" % ( + input_variable_path[0], + current_validations["regex"]["pattern"], + ) + if flags != 0: + # Don't print the regex flags if the flags are not + # specified in the OAS document. + err_msg = r"%s with flags=`%s`" % (err_msg, flags) + raise ApiValueError(err_msg) + + +def order_response_types(required_types): + """Returns the required types sorted in coercion order + + Args: + required_types (list/tuple): collection of classes or instance of + list or dict with class information inside it. + + Returns: + (list): coercion order sorted collection of classes or instance + of list or dict with class information inside it. + """ + + def index_getter(class_or_instance): + if isinstance(class_or_instance, list): + return COERCION_INDEX_BY_TYPE[list] + elif isinstance(class_or_instance, dict): + return COERCION_INDEX_BY_TYPE[dict] + elif inspect.isclass(class_or_instance) and issubclass( + class_or_instance, ModelComposed + ): + return COERCION_INDEX_BY_TYPE[ModelComposed] + elif inspect.isclass(class_or_instance) and issubclass( + class_or_instance, ModelNormal + ): + return COERCION_INDEX_BY_TYPE[ModelNormal] + elif inspect.isclass(class_or_instance) and issubclass( + class_or_instance, ModelSimple + ): + return COERCION_INDEX_BY_TYPE[ModelSimple] + elif class_or_instance in COERCION_INDEX_BY_TYPE: + return COERCION_INDEX_BY_TYPE[class_or_instance] + raise ApiValueError("Unsupported type: %s" % class_or_instance) + + sorted_types = sorted( + required_types, key=lambda class_or_instance: index_getter(class_or_instance) + ) + return sorted_types + + +def remove_uncoercible( + required_types_classes, current_item, spec_property_naming, must_convert=True +): + """Only keeps the type conversions that are possible + + Args: + required_types_classes (tuple): tuple of classes that are required + these should be ordered by COERCION_INDEX_BY_TYPE + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + current_item (any): the current item (input data) to be converted + + Keyword Args: + must_convert (bool): if True the item to convert is of the wrong + type and we want a big list of coercibles + if False, we want a limited list of coercibles + + Returns: + (list): the remaining coercible required types, classes only + """ + current_type_simple = get_simple_class(current_item) + + results_classes = [] + for required_type_class in required_types_classes: + # convert our models to OpenApiModel + required_type_class_simplified = required_type_class + if isinstance(required_type_class_simplified, type): + if issubclass(required_type_class_simplified, ModelComposed): + required_type_class_simplified = ModelComposed + elif issubclass(required_type_class_simplified, ModelNormal): + required_type_class_simplified = ModelNormal + elif issubclass(required_type_class_simplified, ModelSimple): + required_type_class_simplified = ModelSimple + + if required_type_class_simplified == current_type_simple: + # don't consider converting to one's own class + continue + + class_pair = (current_type_simple, required_type_class_simplified) + if must_convert and class_pair in COERCIBLE_TYPE_PAIRS[spec_property_naming]: + results_classes.append(required_type_class) + elif class_pair in UPCONVERSION_TYPE_PAIRS: + results_classes.append(required_type_class) + return results_classes + + +def get_discriminated_classes(cls): + """ + Returns all the classes that a discriminator converts to + TODO: lru_cache this + """ + possible_classes = [] + key = list(cls.discriminator.keys())[0] + if is_type_nullable(cls): + possible_classes.append(cls) + for discr_cls in cls.discriminator[key].values(): + if hasattr(discr_cls, "discriminator") and discr_cls.discriminator is not None: + possible_classes.extend(get_discriminated_classes(discr_cls)) + else: + possible_classes.append(discr_cls) + return possible_classes + + +def get_possible_classes(cls, from_server_context): + # TODO: lru_cache this + possible_classes = [cls] + if from_server_context: + return possible_classes + if hasattr(cls, "discriminator") and cls.discriminator is not None: + possible_classes = [] + possible_classes.extend(get_discriminated_classes(cls)) + elif issubclass(cls, ModelComposed): + possible_classes.extend(composed_model_input_classes(cls)) + return possible_classes + + +def get_required_type_classes(required_types_mixed, spec_property_naming): + """Converts the tuple required_types into a tuple and a dict described + below + + Args: + required_types_mixed (tuple/list): will contain either classes or + instance of list or dict + spec_property_naming (bool): if True these values came from the + server, and we use the data types in our endpoints. + If False, we are client side and we need to include + oneOf and discriminator classes inside the data types in our endpoints + + Returns: + (valid_classes, dict_valid_class_to_child_types_mixed): + valid_classes (tuple): the valid classes that the current item + should be + dict_valid_class_to_child_types_mixed (dict): + valid_class (class): this is the key + child_types_mixed (list/dict/tuple): describes the valid child + types + """ + valid_classes = [] + child_req_types_by_current_type = {} + for required_type in required_types_mixed: + if isinstance(required_type, list): + valid_classes.append(list) + child_req_types_by_current_type[list] = required_type + elif isinstance(required_type, tuple): + valid_classes.append(tuple) + child_req_types_by_current_type[tuple] = required_type + elif isinstance(required_type, dict): + valid_classes.append(dict) + child_req_types_by_current_type[dict] = required_type[str] + else: + valid_classes.extend( + get_possible_classes(required_type, spec_property_naming) + ) + return tuple(valid_classes), child_req_types_by_current_type + + +def change_keys_js_to_python(input_dict, model_class): + """ + Converts from javascript_key keys in the input_dict to python_keys in + the output dict using the mapping in model_class. + If the input_dict contains a key which does not declared in the model_class, + the key is added to the output dict as is. The assumption is the model_class + may have undeclared properties (additionalProperties attribute in the OAS + document). + """ + + if getattr(model_class, "attribute_map", None) is None: + return input_dict + output_dict = {} + reversed_attr_map = {value: key for key, value in model_class.attribute_map.items()} + for javascript_key, value in input_dict.items(): + python_key = reversed_attr_map.get(javascript_key) + if python_key is None: + # if the key is unknown, it is in error or it is an + # additionalProperties variable + python_key = javascript_key + output_dict[python_key] = value + return output_dict + + +def get_type_error(var_value, path_to_item, valid_classes, key_type=False): + error_msg = type_error_message( + var_name=path_to_item[-1], + var_value=var_value, + valid_classes=valid_classes, + key_type=key_type, + ) + return ApiTypeError( + error_msg, + path_to_item=path_to_item, + valid_classes=valid_classes, + key_type=key_type, + ) + + +def deserialize_primitive(data, klass, path_to_item): + """Deserializes string to primitive type. + + :param data: str/int/float + :param klass: str/class the class to convert to + + :return: int, float, str, bool, date, datetime + """ + additional_message = "" + try: + if klass in {datetime, date}: + additional_message = ( + "If you need your parameter to have a fallback " + "string value, please set its type as `type: {}` in your " + "spec. That allows the value to be any type. " + ) + if klass == datetime: + if len(data) < 8: + raise ValueError("This is not a datetime") + # The string should be in iso8601 datetime format. + parsed_datetime = parse(data) + date_only = ( + parsed_datetime.hour == 0 + and parsed_datetime.minute == 0 + and parsed_datetime.second == 0 + and parsed_datetime.tzinfo is None + and 8 <= len(data) <= 10 + ) + if date_only: + raise ValueError("This is a date, not a datetime") + return parsed_datetime + elif klass == date: + if len(data) < 8: + raise ValueError("This is not a date") + return parse(data).date() + else: + converted_value = klass(data) + if isinstance(data, str) and klass == float: + if str(converted_value) != data: + # '7' -> 7.0 -> '7.0' != '7' + raise ValueError("This is not a float") + return converted_value + except (OverflowError, ValueError) as ex: + # parse can raise OverflowError + raise ApiValueError( + "{0}Failed to parse {1} as {2}".format( + additional_message, repr(data), klass.__name__ + ), + path_to_item=path_to_item, + ) from ex + + +def get_discriminator_class(model_class, discr_name, discr_value, cls_visited): + """Returns the child class specified by the discriminator. + + Args: + model_class (OpenApiModel): the model class. + discr_name (string): the name of the discriminator property. + discr_value (any): the discriminator value. + cls_visited (list): list of model classes that have been visited. + Used to determine the discriminator class without + visiting circular references indefinitely. + + Returns: + used_model_class (class/None): the chosen child class that will be used + to deserialize the data, for example dog.Dog. + If a class is not found, None is returned. + """ + + if model_class in cls_visited: + # The class has already been visited and no suitable class was found. + return None + cls_visited.append(model_class) + used_model_class = None + if discr_name in model_class.discriminator: + class_name_to_discr_class = model_class.discriminator[discr_name] + used_model_class = class_name_to_discr_class.get(discr_value) + if used_model_class is None: + # We didn't find a discriminated class in class_name_to_discr_class. + # So look in the ancestor or descendant discriminators + # The discriminator mapping may exist in a descendant (anyOf, oneOf) + # or ancestor (allOf). + # Ancestor example: in the GrandparentAnimal -> ParentPet -> ChildCat + # hierarchy, the discriminator mappings may be defined at any level + # in the hierarchy. + # Descendant example: mammal -> whale/zebra/Pig -> BasquePig/DanishPig + # if we try to make BasquePig from mammal, we need to travel through + # the oneOf descendant discriminators to find BasquePig + descendant_classes = model_class._composed_schemas.get( + "oneOf", () + ) + model_class._composed_schemas.get("anyOf", ()) + ancestor_classes = model_class._composed_schemas.get("allOf", ()) + possible_classes = descendant_classes + ancestor_classes + for cls in possible_classes: + # Check if the schema has inherited discriminators. + if hasattr(cls, "discriminator") and cls.discriminator is not None: + used_model_class = get_discriminator_class( + cls, discr_name, discr_value, cls_visited + ) + if used_model_class is not None: + return used_model_class + return used_model_class + + +def deserialize_model( + model_data, + model_class, + path_to_item, + check_type, + configuration, + spec_property_naming, +): + """Deserializes model_data to model instance. + + Args: + model_data (int/str/float/bool/none_type/list/dict): data to instantiate the model + model_class (OpenApiModel): the model class + path_to_item (list): path to the model in the received data + check_type (bool): whether to check the data tupe for the values in + the model + configuration (Configuration): the instance to use to convert files + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + + Returns: + model instance + + Raise: + ApiTypeError + ApiValueError + ApiKeyError + """ + + kw_args = dict( + _check_type=check_type, + _path_to_item=path_to_item, + _configuration=configuration, + _spec_property_naming=spec_property_naming, + ) + + if issubclass(model_class, ModelSimple): + return model_class._new_from_openapi_data(model_data, **kw_args) + elif isinstance(model_data, list): + return model_class._new_from_openapi_data(*model_data, **kw_args) + if isinstance(model_data, dict): + kw_args.update(model_data) + return model_class._new_from_openapi_data(**kw_args) + elif isinstance(model_data, PRIMITIVE_TYPES): + return model_class._new_from_openapi_data(model_data, **kw_args) + + +def deserialize_file(response_data, configuration, content_disposition=None): + """Deserializes body to file + + Saves response body into a file in a temporary folder, + using the filename from the `Content-Disposition` header if provided. + + Args: + param response_data (str): the file data to write + configuration (Configuration): the instance to use to convert files + + Keyword Args: + content_disposition (str): the value of the Content-Disposition + header + + Returns: + (file_type): the deserialized file which is open + The user is responsible for closing and reading the file + """ + fd, path = tempfile.mkstemp(dir=configuration.temp_folder_path) + os.close(fd) + os.remove(path) + + if content_disposition: + filename = re.search( + r'filename=[\'"]?([^\'"\s]+)[\'"]?', content_disposition, flags=re.I + ) + if filename is not None: + filename = filename.group(1) + else: + filename = "default_" + str(uuid.uuid4()) + + path = os.path.join(os.path.dirname(path), filename) + + with open(path, "wb") as f: + if isinstance(response_data, str): + # change str to bytes so we can write it + response_data = response_data.encode("utf-8") + f.write(response_data) + + f = open(path, "rb") + return f + + +def attempt_convert_item( + input_value, + valid_classes, + path_to_item, + configuration, + spec_property_naming, + key_type=False, + must_convert=False, + check_type=True, +): + """ + Args: + input_value (any): the data to convert + valid_classes (any): the classes that are valid + path_to_item (list): the path to the item to convert + configuration (Configuration): the instance to use to convert files + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + key_type (bool): if True we need to convert a key type (not supported) + must_convert (bool): if True we must convert + check_type (bool): if True we check the type or the returned data in + ModelComposed/ModelNormal/ModelSimple instances + + Returns: + instance (any) the fixed item + + Raises: + ApiTypeError + ApiValueError + ApiKeyError + """ + valid_classes_ordered = order_response_types(valid_classes) + valid_classes_coercible = remove_uncoercible( + valid_classes_ordered, input_value, spec_property_naming + ) + if not valid_classes_coercible or key_type: + # we do not handle keytype errors, json will take care + # of this for us + if configuration is None or not configuration.discard_unknown_keys: + raise get_type_error( + input_value, path_to_item, valid_classes, key_type=key_type + ) + for valid_class in valid_classes_coercible: + try: + if issubclass(valid_class, OpenApiModel): + return deserialize_model( + input_value, + valid_class, + path_to_item, + check_type, + configuration, + spec_property_naming, + ) + elif valid_class == file_type: + return deserialize_file(input_value, configuration) + return deserialize_primitive(input_value, valid_class, path_to_item) + except (ApiTypeError, ApiValueError, ApiKeyError) as conversion_exc: + if must_convert: + raise conversion_exc + # if we have conversion errors when must_convert == False + # we ignore the exception and move on to the next class + continue + # we were unable to convert, must_convert == False + return input_value + + +def is_type_nullable(input_type): + """ + Returns true if None is an allowed value for the specified input_type. + + A type is nullable if at least one of the following conditions is true: + 1. The OAS 'nullable' attribute has been specified, + 1. The type is the 'null' type, + 1. The type is a anyOf/oneOf composed schema, and a child schema is + the 'null' type. + Args: + input_type (type): the class of the input_value that we are + checking + Returns: + bool + """ + if input_type is none_type: + return True + if issubclass(input_type, OpenApiModel) and input_type._nullable: + return True + if issubclass(input_type, ModelComposed): + # If oneOf/anyOf, check if the 'null' type is one of the allowed types. + for t in input_type._composed_schemas.get("oneOf", ()): + if is_type_nullable(t): + return True + for t in input_type._composed_schemas.get("anyOf", ()): + if is_type_nullable(t): + return True + return False + + +def is_valid_type(input_class_simple, valid_classes): + """ + Args: + input_class_simple (class): the class of the input_value that we are + checking + valid_classes (tuple): the valid classes that the current item + should be + Returns: + bool + """ + if issubclass(input_class_simple, OpenApiModel) and valid_classes == ( + bool, + date, + datetime, + dict, + float, + int, + list, + str, + none_type, + ): + return True + valid_type = input_class_simple in valid_classes + if not valid_type and ( + issubclass(input_class_simple, OpenApiModel) or input_class_simple is none_type + ): + for valid_class in valid_classes: + if input_class_simple is none_type and is_type_nullable(valid_class): + # Schema is oneOf/anyOf and the 'null' type is one of the allowed types. + return True + if not ( + issubclass(valid_class, OpenApiModel) and valid_class.discriminator + ): + continue + discr_propertyname_py = list(valid_class.discriminator.keys())[0] + discriminator_classes = valid_class.discriminator[ + discr_propertyname_py + ].values() + valid_type = is_valid_type(input_class_simple, discriminator_classes) + if valid_type: + return True + return valid_type + + +def validate_and_convert_types( + input_value, + required_types_mixed, + path_to_item, + spec_property_naming, + _check_type, + configuration=None, +): + """Raises a TypeError is there is a problem, otherwise returns value + + Args: + input_value (any): the data to validate/convert + required_types_mixed (list/dict/tuple): A list of + valid classes, or a list tuples of valid classes, or a dict where + the value is a tuple of value classes + path_to_item: (list) the path to the data being validated + this stores a list of keys or indices to get to the data being + validated + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + _check_type: (boolean) if true, type will be checked and conversion + will be attempted. + configuration: (Configuration): the configuration class to use + when converting file_type items. + If passed, conversion will be attempted when possible + If not passed, no conversions will be attempted and + exceptions will be raised + + Returns: + the correctly typed value + + Raises: + ApiTypeError + """ + results = get_required_type_classes(required_types_mixed, spec_property_naming) + valid_classes, child_req_types_by_current_type = results + + input_class_simple = get_simple_class(input_value) + valid_type = is_valid_type(input_class_simple, valid_classes) + if not valid_type: + if configuration or (input_class_simple == dict and dict not in valid_classes): + # if input_value is not valid_type try to convert it + converted_instance = attempt_convert_item( + input_value, + valid_classes, + path_to_item, + configuration, + spec_property_naming, + key_type=False, + must_convert=True, + check_type=_check_type, + ) + return converted_instance + else: + raise get_type_error( + input_value, path_to_item, valid_classes, key_type=False + ) + + # input_value's type is in valid_classes + if len(valid_classes) > 1 and configuration: + # there are valid classes which are not the current class + valid_classes_coercible = remove_uncoercible( + valid_classes, input_value, spec_property_naming, must_convert=False + ) + if valid_classes_coercible: + converted_instance = attempt_convert_item( + input_value, + valid_classes_coercible, + path_to_item, + configuration, + spec_property_naming, + key_type=False, + must_convert=False, + check_type=_check_type, + ) + return converted_instance + + if child_req_types_by_current_type == {}: + # all types are of the required types and there are no more inner + # variables left to look at + return input_value + inner_required_types = child_req_types_by_current_type.get(type(input_value)) + if inner_required_types is None: + # for this type, there are not more inner variables left to look at + return input_value + if isinstance(input_value, list): + if input_value == []: + # allow an empty list + return input_value + for index, inner_value in enumerate(input_value): + inner_path = list(path_to_item) + inner_path.append(index) + input_value[index] = validate_and_convert_types( + inner_value, + inner_required_types, + inner_path, + spec_property_naming, + _check_type, + configuration=configuration, + ) + elif isinstance(input_value, dict): + if input_value == {}: + # allow an empty dict + return input_value + for inner_key, inner_val in input_value.items(): + inner_path = list(path_to_item) + inner_path.append(inner_key) + if get_simple_class(inner_key) != str: + raise get_type_error( + inner_key, inner_path, valid_classes, key_type=True + ) + input_value[inner_key] = validate_and_convert_types( + inner_val, + inner_required_types, + inner_path, + spec_property_naming, + _check_type, + configuration=configuration, + ) + return input_value + + +def model_to_dict(model_instance, serialize=True): + """Returns the model properties as a dict + + Args: + model_instance (one of your model instances): the model instance that + will be converted to a dict. + + Keyword Args: + serialize (bool): if True, the keys in the dict will be values from + attribute_map + """ + result = {} + + def extract_item(item): + return ( + (item[0], model_to_dict(item[1], serialize=serialize)) + if hasattr(item[1], "_data_store") + else item + ) + + model_instances = [model_instance] + if model_instance._composed_schemas: + model_instances.extend(model_instance._composed_instances) + seen_json_attribute_names = set() + used_fallback_python_attribute_names = set() + py_to_json_map = {} + for model_instance in model_instances: + for attr, value in model_instance._data_store.items(): + if serialize: + # we use get here because additional property key names do not + # exist in attribute_map + try: + attr = model_instance.attribute_map[attr] + py_to_json_map.update(model_instance.attribute_map) + seen_json_attribute_names.add(attr) + except KeyError: + used_fallback_python_attribute_names.add(attr) + if isinstance(value, list): + if not value: + # empty list or None + result[attr] = value + else: + res = [] + for v in value: + if isinstance(v, PRIMITIVE_TYPES) or v is None: + res.append(v) + elif isinstance(v, ModelSimple): + res.append(v.value) + elif isinstance(v, dict): + res.append(dict(map(extract_item, v.items()))) + else: + res.append(model_to_dict(v, serialize=serialize)) + result[attr] = res + elif isinstance(value, dict): + result[attr] = dict(map(extract_item, value.items())) + elif isinstance(value, ModelSimple): + result[attr] = value.value + elif hasattr(value, "_data_store"): + result[attr] = model_to_dict(value, serialize=serialize) + else: + result[attr] = value + if serialize: + for python_key in used_fallback_python_attribute_names: + json_key = py_to_json_map.get(python_key) + if json_key is None: + continue + if python_key == json_key: + continue + json_key_assigned_no_need_for_python_key = ( + json_key in seen_json_attribute_names + ) + if json_key_assigned_no_need_for_python_key: + del result[python_key] + + return result + + +def type_error_message( + var_value=None, var_name=None, valid_classes=None, key_type=None +): + """ + Keyword Args: + var_value (any): the variable which has the type_error + var_name (str): the name of the variable which has the typ error + valid_classes (tuple): the accepted classes for current_item's + value + key_type (bool): False if our value is a value in a dict + True if it is a key in a dict + False if our item is an item in a list + """ + key_or_value = "value" + if key_type: + key_or_value = "key" + valid_classes_phrase = get_valid_classes_phrase(valid_classes) + msg = ( + "Invalid type for variable '{0}'. Required {1} type {2} and " + "passed type was {3}".format( + var_name, + key_or_value, + valid_classes_phrase, + type(var_value).__name__, + ) + ) + return msg + + +def get_valid_classes_phrase(input_classes): + """Returns a string phrase describing what types are allowed""" + all_classes = list(input_classes) + all_classes = sorted(all_classes, key=lambda cls: cls.__name__) + all_class_names = [cls.__name__ for cls in all_classes] + if len(all_class_names) == 1: + return "is {0}".format(all_class_names[0]) + return "is one of [{0}]".format(", ".join(all_class_names)) + + +def get_allof_instances(self, model_args, constant_args): + """ + Args: + self: the class we are handling + model_args (dict): var_name to var_value + used to make instances + constant_args (dict): + metadata arguments: + _check_type + _path_to_item + _spec_property_naming + _configuration + _visited_composed_classes + + Returns + composed_instances (list) + """ + composed_instances = [] + for allof_class in self._composed_schemas["allOf"]: + + try: + if constant_args.get("_spec_property_naming"): + allof_instance = allof_class._from_openapi_data( + **model_args, **constant_args + ) + else: + allof_instance = allof_class(**model_args, **constant_args) + composed_instances.append(allof_instance) + except Exception as ex: + raise ApiValueError( + "Invalid inputs given to generate an instance of '%s'. The " + "input data was invalid for the allOf schema '%s' in the composed " + "schema '%s'. Error=%s" + % ( + allof_class.__name__, + allof_class.__name__, + self.__class__.__name__, + str(ex), + ) + ) from ex + return composed_instances + + +def get_oneof_instance(cls, model_kwargs, constant_kwargs, model_arg=None): + """ + Find the oneOf schema that matches the input data (e.g. payload). + If exactly one schema matches the input data, an instance of that schema + is returned. + If zero or more than one schema match the input data, an exception is raised. + In OAS 3.x, the payload MUST, by validation, match exactly one of the + schemas described by oneOf. + + Args: + cls: the class we are handling + model_kwargs (dict): var_name to var_value + The input data, e.g. the payload that must match a oneOf schema + in the OpenAPI document. + constant_kwargs (dict): var_name to var_value + args that every model requires, including configuration, server + and path to item. + + Kwargs: + model_arg: (int, float, bool, str, date, datetime, ModelSimple, None): + the value to assign to a primitive class or ModelSimple class + Notes: + - this is only passed in when oneOf includes types which are not object + - None is used to suppress handling of model_arg, nullable models are handled in __new__ + + Returns + oneof_instance (instance) + """ + if len(cls._composed_schemas["oneOf"]) == 0: + return None + + oneof_instances = [] + # Iterate over each oneOf schema and determine if the input data + # matches the oneOf schemas. + for oneof_class in cls._composed_schemas["oneOf"]: + # The composed oneOf schema allows the 'null' type and the input data + # is the null value. This is a OAS >= 3.1 feature. + if oneof_class is none_type: + # skip none_types because we are deserializing dict data. + # none_type deserialization is handled in the __new__ method + continue + + single_value_input = allows_single_value_input(oneof_class) + + try: + if not single_value_input: + if constant_kwargs.get("_spec_property_naming"): + oneof_instance = oneof_class._from_openapi_data( + **model_kwargs, **constant_kwargs + ) + else: + oneof_instance = oneof_class(**model_kwargs, **constant_kwargs) + else: + if issubclass(oneof_class, ModelSimple): + if constant_kwargs.get("_spec_property_naming"): + oneof_instance = oneof_class._from_openapi_data( + model_arg, **constant_kwargs + ) + else: + oneof_instance = oneof_class(model_arg, **constant_kwargs) + elif oneof_class in PRIMITIVE_TYPES: + oneof_instance = validate_and_convert_types( + model_arg, + (oneof_class,), + constant_kwargs["_path_to_item"], + constant_kwargs["_spec_property_naming"], + constant_kwargs["_check_type"], + configuration=constant_kwargs["_configuration"], + ) + oneof_instances.append(oneof_instance) + except Exception: + pass + if len(oneof_instances) == 0: + raise ApiValueError( + "Invalid inputs given to generate an instance of %s. None " + "of the oneOf schemas matched the input data." % cls.__name__ + ) + elif len(oneof_instances) > 1: + raise ApiValueError( + "Invalid inputs given to generate an instance of %s. Multiple " + "oneOf schemas matched the inputs, but a max of one is allowed." + % cls.__name__ + ) + return oneof_instances[0] + + +def get_anyof_instances(self, model_args, constant_args): + """ + Args: + self: the class we are handling + model_args (dict): var_name to var_value + The input data, e.g. the payload that must match at least one + anyOf child schema in the OpenAPI document. + constant_args (dict): var_name to var_value + args that every model requires, including configuration, server + and path to item. + + Returns + anyof_instances (list) + """ + anyof_instances = [] + if len(self._composed_schemas["anyOf"]) == 0: + return anyof_instances + + for anyof_class in self._composed_schemas["anyOf"]: + # The composed oneOf schema allows the 'null' type and the input data + # is the null value. This is a OAS >= 3.1 feature. + if anyof_class is none_type: + # skip none_types because we are deserializing dict data. + # none_type deserialization is handled in the __new__ method + continue + + try: + if constant_args.get("_spec_property_naming"): + anyof_instance = anyof_class._from_openapi_data( + **model_args, **constant_args + ) + else: + anyof_instance = anyof_class(**model_args, **constant_args) + anyof_instances.append(anyof_instance) + except Exception: + pass + if len(anyof_instances) == 0: + raise ApiValueError( + "Invalid inputs given to generate an instance of %s. None of the " + "anyOf schemas matched the inputs." % self.__class__.__name__ + ) + return anyof_instances + + +def get_discarded_args(self, composed_instances, model_args): + """ + Gathers the args that were discarded by configuration.discard_unknown_keys + """ + model_arg_keys = model_args.keys() + discarded_args = set() + # arguments passed to self were already converted to python names + # before __init__ was called + for instance in composed_instances: + if instance.__class__ in self._composed_schemas["allOf"]: + try: + keys = instance.to_dict().keys() + discarded_keys = model_args - keys + discarded_args.update(discarded_keys) + except Exception: + # allOf integer schema will throw exception + pass + else: + try: + all_keys = set(model_to_dict(instance, serialize=False).keys()) + js_keys = model_to_dict(instance, serialize=True).keys() + all_keys.update(js_keys) + discarded_keys = model_arg_keys - all_keys + discarded_args.update(discarded_keys) + except Exception: + # allOf integer schema will throw exception + pass + return discarded_args + + +def validate_get_composed_info(constant_args, model_args, self): + """ + For composed schemas, generate schema instances for + all schemas in the oneOf/anyOf/allOf definition. If additional + properties are allowed, also assign those properties on + all matched schemas that contain additionalProperties. + Openapi schemas are python classes. + + Exceptions are raised if: + - 0 or > 1 oneOf schema matches the model_args input data + - no anyOf schema matches the model_args input data + - any of the allOf schemas do not match the model_args input data + + Args: + constant_args (dict): these are the args that every model requires + model_args (dict): these are the required and optional spec args that + were passed in to make this model + self (class): the class that we are instantiating + This class contains self._composed_schemas + + Returns: + composed_info (list): length three + composed_instances (list): the composed instances which are not + self + var_name_to_model_instances (dict): a dict going from var_name + to the model_instance which holds that var_name + the model_instance may be self or an instance of one of the + classes in self.composed_instances() + additional_properties_model_instances (list): a list of the + model instances which have the property + additional_properties_type. This list can include self + """ + # create composed_instances + composed_instances = [] + allof_instances = get_allof_instances(self, model_args, constant_args) + composed_instances.extend(allof_instances) + oneof_instance = get_oneof_instance(self.__class__, model_args, constant_args) + if oneof_instance is not None: + composed_instances.append(oneof_instance) + anyof_instances = get_anyof_instances(self, model_args, constant_args) + composed_instances.extend(anyof_instances) + """ + set additional_properties_model_instances + additional properties must be evaluated at the schema level + so self's additional properties are most important + If self is a composed schema with: + - no properties defined in self + - additionalProperties: False + Then for object payloads every property is an additional property + and they are not allowed, so only empty dict is allowed + + Properties must be set on all matching schemas + so when a property is assigned toa composed instance, it must be set on all + composed instances regardless of additionalProperties presence + keeping it to prevent breaking changes in v5.0.1 + TODO remove cls._additional_properties_model_instances in 6.0.0 + """ + additional_properties_model_instances = [] + if self.additional_properties_type is not None: + additional_properties_model_instances = [self] + + """ + no need to set properties on self in here, they will be set in __init__ + By here all composed schema oneOf/anyOf/allOf instances have their properties set using + model_args + """ + discarded_args = get_discarded_args(self, composed_instances, model_args) + + # map variable names to composed_instances + var_name_to_model_instances = {} + for prop_name in model_args: + if prop_name not in discarded_args: + var_name_to_model_instances[prop_name] = [self] + list( + filter(lambda x: prop_name in x.openapi_types, composed_instances) + ) + + return [ + composed_instances, + var_name_to_model_instances, + additional_properties_model_instances, + discarded_args, + ] diff --git a/python-client/deutschland/bundestag_tagesordnung/models/__init__.py b/python-client/deutschland/bundestag_tagesordnung/models/__init__.py new file mode 100644 index 0000000..5a52ab2 --- /dev/null +++ b/python-client/deutschland/bundestag_tagesordnung/models/__init__.py @@ -0,0 +1,10 @@ +# flake8: noqa + +# import all models into this package +# if you have many models here with many references from one model to another this may +# raise a RecursionError +# to avoid this, import only the models that you directly need like: +# from from deutschland.bundestag_tagesordnung.model.pet import Pet +# or import this package, but before doing it, use: +# import sys +# sys.setrecursionlimit(n) diff --git a/python-client/deutschland/bundestag_tagesordnung/rest.py b/python-client/deutschland/bundestag_tagesordnung/rest.py new file mode 100644 index 0000000..38d8df2 --- /dev/null +++ b/python-client/deutschland/bundestag_tagesordnung/rest.py @@ -0,0 +1,450 @@ +""" + Tagesordnungen API + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Contact: kontakt@bund.dev + Generated by: https://openapi-generator.tech +""" + +import io +import ipaddress +import json +import logging +import re +import ssl +from urllib.parse import urlencode, urlparse +from urllib.request import proxy_bypass_environment + +import urllib3 +from deutschland.bundestag_tagesordnung.exceptions import ( + ApiException, + ApiValueError, + ForbiddenException, + NotFoundException, + ServiceException, + UnauthorizedException, +) + +logger = logging.getLogger(__name__) + + +class RESTResponse(io.IOBase): + + def __init__(self, resp): + self.urllib3_response = resp + self.status = resp.status + self.reason = resp.reason + self.data = resp.data + + def getheaders(self): + """Returns a dictionary of the response headers.""" + return self.urllib3_response.getheaders() + + def getheader(self, name, default=None): + """Returns a given response header.""" + return self.urllib3_response.getheader(name, default) + + +class RESTClientObject(object): + + def __init__(self, configuration, pools_size=4, maxsize=None): + # urllib3.PoolManager will pass all kw parameters to connectionpool + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # noqa: E501 + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # noqa: E501 + # maxsize is the number of requests to host that are allowed in parallel # noqa: E501 + # Custom SSL certificates and client certificates: http://urllib3.readthedocs.io/en/latest/advanced-usage.html # noqa: E501 + + # cert_reqs + if configuration.verify_ssl: + cert_reqs = ssl.CERT_REQUIRED + else: + cert_reqs = ssl.CERT_NONE + + addition_pool_args = {} + if configuration.assert_hostname is not None: + addition_pool_args["assert_hostname"] = ( + configuration.assert_hostname + ) # noqa: E501 + + if configuration.retries is not None: + addition_pool_args["retries"] = configuration.retries + + if configuration.socket_options is not None: + addition_pool_args["socket_options"] = configuration.socket_options + + if maxsize is None: + if configuration.connection_pool_maxsize is not None: + maxsize = configuration.connection_pool_maxsize + else: + maxsize = 4 + + # https pool manager + if configuration.proxy and not should_bypass_proxies( + configuration.host, no_proxy=configuration.no_proxy or "" + ): + self.pool_manager = urllib3.ProxyManager( + num_pools=pools_size, + maxsize=maxsize, + cert_reqs=cert_reqs, + ca_certs=configuration.ssl_ca_cert, + cert_file=configuration.cert_file, + key_file=configuration.key_file, + proxy_url=configuration.proxy, + proxy_headers=configuration.proxy_headers, + **addition_pool_args + ) + else: + self.pool_manager = urllib3.PoolManager( + num_pools=pools_size, + maxsize=maxsize, + cert_reqs=cert_reqs, + ca_certs=configuration.ssl_ca_cert, + cert_file=configuration.cert_file, + key_file=configuration.key_file, + **addition_pool_args + ) + + def request( + self, + method, + url, + query_params=None, + headers=None, + body=None, + post_params=None, + _preload_content=True, + _request_timeout=None, + ): + """Perform requests. + + :param method: http request method + :param url: http request url + :param query_params: query parameters in the url + :param headers: http request headers + :param body: request json body, for `application/json` + :param post_params: request post parameters, + `application/x-www-form-urlencoded` + and `multipart/form-data` + :param _preload_content: if False, the urllib3.HTTPResponse object will + be returned without reading/decoding response + data. Default is True. + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + """ + method = method.upper() + assert method in ["GET", "HEAD", "DELETE", "POST", "PUT", "PATCH", "OPTIONS"] + + if post_params and body: + raise ApiValueError( + "body parameter cannot be used with post_params parameter." + ) + + post_params = post_params or {} + headers = headers or {} + + timeout = None + if _request_timeout: + if isinstance(_request_timeout, (int, float)): # noqa: E501,F821 + timeout = urllib3.Timeout(total=_request_timeout) + elif isinstance(_request_timeout, tuple) and len(_request_timeout) == 2: + timeout = urllib3.Timeout( + connect=_request_timeout[0], read=_request_timeout[1] + ) + + try: + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` + if method in ["POST", "PUT", "PATCH", "OPTIONS", "DELETE"]: + # Only set a default Content-Type for POST, PUT, PATCH and OPTIONS requests + if (method != "DELETE") and ("Content-Type" not in headers): + headers["Content-Type"] = "application/json" + if query_params: + url += "?" + urlencode(query_params) + if ("Content-Type" not in headers) or ( + re.search("json", headers["Content-Type"], re.IGNORECASE) + ): + request_body = None + if body is not None: + request_body = json.dumps(body) + r = self.pool_manager.request( + method, + url, + body=request_body, + preload_content=_preload_content, + timeout=timeout, + headers=headers, + ) + elif ( + headers["Content-Type"] == "application/x-www-form-urlencoded" + ): # noqa: E501 + r = self.pool_manager.request( + method, + url, + fields=post_params, + encode_multipart=False, + preload_content=_preload_content, + timeout=timeout, + headers=headers, + ) + elif headers["Content-Type"] == "multipart/form-data": + # must del headers['Content-Type'], or the correct + # Content-Type which generated by urllib3 will be + # overwritten. + del headers["Content-Type"] + r = self.pool_manager.request( + method, + url, + fields=post_params, + encode_multipart=True, + preload_content=_preload_content, + timeout=timeout, + headers=headers, + ) + # Pass a `string` parameter directly in the body to support + # other content types than Json when `body` argument is + # provided in serialized form + elif isinstance(body, str) or isinstance(body, bytes): + request_body = body + r = self.pool_manager.request( + method, + url, + body=request_body, + preload_content=_preload_content, + timeout=timeout, + headers=headers, + ) + else: + # Cannot generate the request from given parameters + msg = """Cannot prepare a request message for provided + arguments. Please check that your arguments match + declared content type.""" + raise ApiException(status=0, reason=msg) + # For `GET`, `HEAD` + else: + r = self.pool_manager.request( + method, + url, + fields=query_params, + preload_content=_preload_content, + timeout=timeout, + headers=headers, + ) + except urllib3.exceptions.SSLError as e: + msg = "{0}\n{1}".format(type(e).__name__, str(e)) + raise ApiException(status=0, reason=msg) + + if _preload_content: + r = RESTResponse(r) + + # log response body + logger.debug("response body: %s", r.data) + + if not 200 <= r.status <= 299: + if r.status == 401: + raise UnauthorizedException(http_resp=r) + + if r.status == 403: + raise ForbiddenException(http_resp=r) + + if r.status == 404: + raise NotFoundException(http_resp=r) + + if 500 <= r.status <= 599: + raise ServiceException(http_resp=r) + + raise ApiException(http_resp=r) + + return r + + def GET( + self, + url, + headers=None, + query_params=None, + _preload_content=True, + _request_timeout=None, + ): + return self.request( + "GET", + url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params, + ) + + def HEAD( + self, + url, + headers=None, + query_params=None, + _preload_content=True, + _request_timeout=None, + ): + return self.request( + "HEAD", + url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params, + ) + + def OPTIONS( + self, + url, + headers=None, + query_params=None, + post_params=None, + body=None, + _preload_content=True, + _request_timeout=None, + ): + return self.request( + "OPTIONS", + url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body, + ) + + def DELETE( + self, + url, + headers=None, + query_params=None, + body=None, + _preload_content=True, + _request_timeout=None, + ): + return self.request( + "DELETE", + url, + headers=headers, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body, + ) + + def POST( + self, + url, + headers=None, + query_params=None, + post_params=None, + body=None, + _preload_content=True, + _request_timeout=None, + ): + return self.request( + "POST", + url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body, + ) + + def PUT( + self, + url, + headers=None, + query_params=None, + post_params=None, + body=None, + _preload_content=True, + _request_timeout=None, + ): + return self.request( + "PUT", + url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body, + ) + + def PATCH( + self, + url, + headers=None, + query_params=None, + post_params=None, + body=None, + _preload_content=True, + _request_timeout=None, + ): + return self.request( + "PATCH", + url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body, + ) + + +# end of class RESTClientObject + + +def is_ipv4(target): + """Test if IPv4 address or not""" + try: + chk = ipaddress.IPv4Address(target) + return True + except ipaddress.AddressValueError: + return False + + +def in_ipv4net(target, net): + """Test if target belongs to given IPv4 network""" + try: + nw = ipaddress.IPv4Network(net) + ip = ipaddress.IPv4Address(target) + if ip in nw: + return True + return False + except ipaddress.AddressValueError: + return False + except ipaddress.NetmaskValueError: + return False + + +def should_bypass_proxies(url, no_proxy=None): + """Yet another requests.should_bypass_proxies + Test if proxies should not be used for a particular url. + """ + + parsed = urlparse(url) + + # special cases + if parsed.hostname in [None, ""]: + return True + + # special cases + if no_proxy in [None, ""]: + return False + if no_proxy == "*": + return True + + no_proxy = no_proxy.lower().replace(" ", "") + entries = (host for host in no_proxy.split(",") if host) + + if is_ipv4(parsed.hostname): + for item in entries: + if in_ipv4net(parsed.hostname, item): + return True + return proxy_bypass_environment(parsed.hostname, {"no": no_proxy}) diff --git a/python-client/docs/DefaultApi.md b/python-client/docs/DefaultApi.md new file mode 100644 index 0000000..5593717 --- /dev/null +++ b/python-client/docs/DefaultApi.md @@ -0,0 +1,280 @@ +# bundestag_tagesordnung.DefaultApi + +All URIs are relative to *https://api.hutt.io* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**bt_to_csv_get**](DefaultApi.md#bt_to_csv_get) | **GET** /bt-to/csv | Tagesordnungen im CSV-Format abrufen +[**bt_to_ical_get**](DefaultApi.md#bt_to_ical_get) | **GET** /bt-to/ical | Tagesordnungen im iCal-Format abrufen +[**bt_to_json_get**](DefaultApi.md#bt_to_json_get) | **GET** /bt-to/json | Tagesordnungen im JSON-Format abrufen +[**bt_to_xml_get**](DefaultApi.md#bt_to_xml_get) | **GET** /bt-to/xml | Tagesordnungen im XML-Format abrufen + + +# **bt_to_csv_get** +> str bt_to_csv_get() + +Tagesordnungen im CSV-Format abrufen + +### Example + + +```python +import time +from deutschland import bundestag_tagesordnung +from deutschland.bundestag_tagesordnung.api import default_api +from pprint import pprint +# Defining the host is optional and defaults to https://api.hutt.io +# See configuration.py for a list of all supported configuration parameters. +configuration = bundestag_tagesordnung.Configuration( + host = "https://api.hutt.io" +) + + +# Enter a context with an instance of the API client +with bundestag_tagesordnung.ApiClient() as api_client: + # Create an instance of the API class + api_instance = default_api.DefaultApi(api_client) + year = 1 # int | Das Jahr, für das Tagesordnungen abgerufen werden sollen (optional) (optional) + week = 1 # int | Die Kalenderwoche, für die Tagesordnungen abgerufen werden sollen (optional; kann mit Jahr kombiniert werden) (optional) + + # example passing only required values which don't have defaults set + # and optional values + try: + # Tagesordnungen im CSV-Format abrufen + api_response = api_instance.bt_to_csv_get(year=year, week=week) + pprint(api_response) + except bundestag_tagesordnung.ApiException as e: + print("Exception when calling DefaultApi->bt_to_csv_get: %s\n" % e) +``` + + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **year** | **int**| Das Jahr, für das Tagesordnungen abgerufen werden sollen (optional) | [optional] + **week** | **int**| Die Kalenderwoche, für die Tagesordnungen abgerufen werden sollen (optional; kann mit Jahr kombiniert werden) | [optional] + +### Return type + +**str** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: text/csv + + +### HTTP response details + +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | Erfolgreiche Antwort | - | + +[[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) + +# **bt_to_ical_get** +> str bt_to_ical_get() + +Tagesordnungen im iCal-Format abrufen + +### Example + + +```python +import time +from deutschland import bundestag_tagesordnung +from deutschland.bundestag_tagesordnung.api import default_api +from pprint import pprint +# Defining the host is optional and defaults to https://api.hutt.io +# See configuration.py for a list of all supported configuration parameters. +configuration = bundestag_tagesordnung.Configuration( + host = "https://api.hutt.io" +) + + +# Enter a context with an instance of the API client +with bundestag_tagesordnung.ApiClient() as api_client: + # Create an instance of the API class + api_instance = default_api.DefaultApi(api_client) + na = True # bool | Zusätzliche Kalendereinträge für Namentliche Abstimmungen erstellen (optional; kombinierbar) (optional) + na_alarm = True # bool | Alarme 15 Minuten vor Namentlichen Abstimmungen hinzufügen (optional; kombinierbar) (optional) + show_sw = True # bool | Ganztägige Termine für Sitzungswochen erstellen (optional; kombinierbar) (optional) + + # example passing only required values which don't have defaults set + # and optional values + try: + # Tagesordnungen im iCal-Format abrufen + api_response = api_instance.bt_to_ical_get(na=na, na_alarm=na_alarm, show_sw=show_sw) + pprint(api_response) + except bundestag_tagesordnung.ApiException as e: + print("Exception when calling DefaultApi->bt_to_ical_get: %s\n" % e) +``` + + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **na** | **bool**| Zusätzliche Kalendereinträge für Namentliche Abstimmungen erstellen (optional; kombinierbar) | [optional] + **na_alarm** | **bool**| Alarme 15 Minuten vor Namentlichen Abstimmungen hinzufügen (optional; kombinierbar) | [optional] + **show_sw** | **bool**| Ganztägige Termine für Sitzungswochen erstellen (optional; kombinierbar) | [optional] + +### Return type + +**str** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: text/calendar + + +### HTTP response details + +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | Erfolgreiche Antwort | - | + +[[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) + +# **bt_to_json_get** +> [{str: (bool, date, datetime, dict, float, int, list, str, none_type)}] bt_to_json_get() + +Tagesordnungen im JSON-Format abrufen + +### Example + + +```python +import time +from deutschland import bundestag_tagesordnung +from deutschland.bundestag_tagesordnung.api import default_api +from pprint import pprint +# Defining the host is optional and defaults to https://api.hutt.io +# See configuration.py for a list of all supported configuration parameters. +configuration = bundestag_tagesordnung.Configuration( + host = "https://api.hutt.io" +) + + +# Enter a context with an instance of the API client +with bundestag_tagesordnung.ApiClient() as api_client: + # Create an instance of the API class + api_instance = default_api.DefaultApi(api_client) + week = 1 # int | Die Kalenderwoche, für die Tagesordnungen abgerufen werden sollen (optional) (optional) + + # example passing only required values which don't have defaults set + # and optional values + try: + # Tagesordnungen im JSON-Format abrufen + api_response = api_instance.bt_to_json_get(week=week) + pprint(api_response) + except bundestag_tagesordnung.ApiException as e: + print("Exception when calling DefaultApi->bt_to_json_get: %s\n" % e) +``` + + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **week** | **int**| Die Kalenderwoche, für die Tagesordnungen abgerufen werden sollen (optional) | [optional] + +### Return type + +**[{str: (bool, date, datetime, dict, float, int, list, str, none_type)}]** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + + +### HTTP response details + +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | Erfolgreiche Antwort | - | + +[[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) + +# **bt_to_xml_get** +> str bt_to_xml_get() + +Tagesordnungen im XML-Format abrufen + +### Example + + +```python +import time +from deutschland import bundestag_tagesordnung +from deutschland.bundestag_tagesordnung.api import default_api +from pprint import pprint +# Defining the host is optional and defaults to https://api.hutt.io +# See configuration.py for a list of all supported configuration parameters. +configuration = bundestag_tagesordnung.Configuration( + host = "https://api.hutt.io" +) + + +# Enter a context with an instance of the API client +with bundestag_tagesordnung.ApiClient() as api_client: + # Create an instance of the API class + api_instance = default_api.DefaultApi(api_client) + year = 1 # int | Das Jahr, für das Tagesordnungen abgerufen werden sollen (optional) (optional) + week = 1 # int | Die Kalenderwoche, für die Tagesordnungen abgerufen werden sollen (optional; kann mit Jahr kombiniert werden) (optional) + + # example passing only required values which don't have defaults set + # and optional values + try: + # Tagesordnungen im XML-Format abrufen + api_response = api_instance.bt_to_xml_get(year=year, week=week) + pprint(api_response) + except bundestag_tagesordnung.ApiException as e: + print("Exception when calling DefaultApi->bt_to_xml_get: %s\n" % e) +``` + + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **year** | **int**| Das Jahr, für das Tagesordnungen abgerufen werden sollen (optional) | [optional] + **week** | **int**| Die Kalenderwoche, für die Tagesordnungen abgerufen werden sollen (optional; kann mit Jahr kombiniert werden) | [optional] + +### Return type + +**str** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/xml + + +### HTTP response details + +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | Erfolgreiche Antwort | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/python-client/pyproject.toml b/python-client/pyproject.toml new file mode 100644 index 0000000..1f49483 --- /dev/null +++ b/python-client/pyproject.toml @@ -0,0 +1,31 @@ +[tool] +[tool.poetry] +name = "de-bundestag-tagesordnung" +version = "1.0.0" +description = "Tagesordnungen API" +keywords = ["OpenAPI", "OpenAPI-Generator", "bundestag-tagesordnung", "App", "API"] +homepage = "https://github.com/bundesAPI/bundestag-tagesordnung-api" +authors = ["BundesAPI "] +packages = [ + { include = "deutschland"} +] +license = "Apache-2.0" +readme = "README.md" + +[tool.poetry.dependencies] +python = ">=3.6" +python-dateutil = "*" +urllib3 = ">=1.25.3" + +[tool.poetry.urls] +"Bug Tracker" = "https://github.com/bundesAPI/bundestag-tagesordnung-api/issues" + +[tool.poetry.dev-dependencies] +black = "^21.7b0" +pytest = "^6.2.4" + + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + diff --git a/python-client/requirements.txt b/python-client/requirements.txt new file mode 100644 index 0000000..9aa85a6 --- /dev/null +++ b/python-client/requirements.txt @@ -0,0 +1,3 @@ +python_dateutil >= 2.5.3 +setuptools >= 59.0.0 +urllib3 >= 1.25.3 diff --git a/python-client/sphinx-docs/Makefile b/python-client/sphinx-docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/python-client/sphinx-docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/python-client/sphinx-docs/conf.py b/python-client/sphinx-docs/conf.py new file mode 100644 index 0000000..eb03222 --- /dev/null +++ b/python-client/sphinx-docs/conf.py @@ -0,0 +1,41 @@ +import os +import sys + +sys.path.insert(0, os.path.abspath("../")) +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = "bundestag_tagesordnung-api" +copyright = "2024, BundesAPI" +author = "BundesAPI" + +version = "1.0.0" +release = "1.0.0" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "m2r2", + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.autosummary", +] + +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +language = "de" + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "alabaster" +html_static_path = ["_static"] + +source_extensions = [".rst", ".md"] diff --git a/python-client/sphinx-docs/index.rst b/python-client/sphinx-docs/index.rst new file mode 100644 index 0000000..f6d1579 --- /dev/null +++ b/python-client/sphinx-docs/index.rst @@ -0,0 +1,7 @@ +bundestag_tagesordnung-api Documentation +======================================== + +.. toctree:: + :glob: + + source/* diff --git a/python-client/sphinx-docs/make.bat b/python-client/sphinx-docs/make.bat new file mode 100644 index 0000000..32bb245 --- /dev/null +++ b/python-client/sphinx-docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/python-client/sphinx-docs/source/bundestag_tagesordnung.api.rst b/python-client/sphinx-docs/source/bundestag_tagesordnung.api.rst new file mode 100644 index 0000000..b03ab2e --- /dev/null +++ b/python-client/sphinx-docs/source/bundestag_tagesordnung.api.rst @@ -0,0 +1,21 @@ +bundestag\_tagesordnung.api package +=================================== + +Submodules +---------- + +bundestag\_tagesordnung.api.default\_api module +----------------------------------------------- + +.. automodule:: bundestag_tagesordnung.api.default_api + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: bundestag_tagesordnung.api + :members: + :undoc-members: + :show-inheritance: diff --git a/python-client/sphinx-docs/source/bundestag_tagesordnung.apis.rst b/python-client/sphinx-docs/source/bundestag_tagesordnung.apis.rst new file mode 100644 index 0000000..9dacfa8 --- /dev/null +++ b/python-client/sphinx-docs/source/bundestag_tagesordnung.apis.rst @@ -0,0 +1,10 @@ +bundestag\_tagesordnung.apis package +==================================== + +Module contents +--------------- + +.. automodule:: bundestag_tagesordnung.apis + :members: + :undoc-members: + :show-inheritance: diff --git a/python-client/sphinx-docs/source/bundestag_tagesordnung.model.rst b/python-client/sphinx-docs/source/bundestag_tagesordnung.model.rst new file mode 100644 index 0000000..bf8a26d --- /dev/null +++ b/python-client/sphinx-docs/source/bundestag_tagesordnung.model.rst @@ -0,0 +1,10 @@ +bundestag\_tagesordnung.model package +===================================== + +Module contents +--------------- + +.. automodule:: bundestag_tagesordnung.model + :members: + :undoc-members: + :show-inheritance: diff --git a/python-client/sphinx-docs/source/bundestag_tagesordnung.models.rst b/python-client/sphinx-docs/source/bundestag_tagesordnung.models.rst new file mode 100644 index 0000000..307044e --- /dev/null +++ b/python-client/sphinx-docs/source/bundestag_tagesordnung.models.rst @@ -0,0 +1,10 @@ +bundestag\_tagesordnung.models package +====================================== + +Module contents +--------------- + +.. automodule:: bundestag_tagesordnung.models + :members: + :undoc-members: + :show-inheritance: diff --git a/python-client/sphinx-docs/source/bundestag_tagesordnung.rst b/python-client/sphinx-docs/source/bundestag_tagesordnung.rst new file mode 100644 index 0000000..efbf0f5 --- /dev/null +++ b/python-client/sphinx-docs/source/bundestag_tagesordnung.rst @@ -0,0 +1,64 @@ +bundestag\_tagesordnung package +=============================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + bundestag_tagesordnung.api + bundestag_tagesordnung.apis + bundestag_tagesordnung.model + bundestag_tagesordnung.models + +Submodules +---------- + +bundestag\_tagesordnung.api\_client module +------------------------------------------ + +.. automodule:: bundestag_tagesordnung.api_client + :members: + :undoc-members: + :show-inheritance: + +bundestag\_tagesordnung.configuration module +-------------------------------------------- + +.. automodule:: bundestag_tagesordnung.configuration + :members: + :undoc-members: + :show-inheritance: + +bundestag\_tagesordnung.exceptions module +----------------------------------------- + +.. automodule:: bundestag_tagesordnung.exceptions + :members: + :undoc-members: + :show-inheritance: + +bundestag\_tagesordnung.model\_utils module +------------------------------------------- + +.. automodule:: bundestag_tagesordnung.model_utils + :members: + :undoc-members: + :show-inheritance: + +bundestag\_tagesordnung.rest module +----------------------------------- + +.. automodule:: bundestag_tagesordnung.rest + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: bundestag_tagesordnung + :members: + :undoc-members: + :show-inheritance: diff --git a/python-client/sphinx-docs/source/modules.rst b/python-client/sphinx-docs/source/modules.rst new file mode 100644 index 0000000..53861c4 --- /dev/null +++ b/python-client/sphinx-docs/source/modules.rst @@ -0,0 +1,7 @@ +deutschland +=========== + +.. toctree:: + :maxdepth: 4 + + bundestag_tagesordnung diff --git a/python-client/test-requirements.txt b/python-client/test-requirements.txt new file mode 100644 index 0000000..bb4f22b --- /dev/null +++ b/python-client/test-requirements.txt @@ -0,0 +1 @@ +pytest-cov>=2.8.1 diff --git a/python-client/test/__init__.py b/python-client/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python-client/test/test_default_api.py b/python-client/test/test_default_api.py new file mode 100644 index 0000000..d290133 --- /dev/null +++ b/python-client/test/test_default_api.py @@ -0,0 +1,57 @@ +""" + Tagesordnungen API + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Contact: kontakt@bund.dev + Generated by: https://openapi-generator.tech +""" + +import unittest + +from deutschland.bundestag_tagesordnung.api.default_api import DefaultApi # noqa: E501 + +from deutschland import bundestag_tagesordnung + + +class TestDefaultApi(unittest.TestCase): + """DefaultApi unit test stubs""" + + def setUp(self): + self.api = DefaultApi() # noqa: E501 + + def tearDown(self): + pass + + def test_bt_to_csv_get(self): + """Test case for bt_to_csv_get + + Tagesordnungen im CSV-Format abrufen # noqa: E501 + """ + pass + + def test_bt_to_ical_get(self): + """Test case for bt_to_ical_get + + Tagesordnungen im iCal-Format abrufen # noqa: E501 + """ + pass + + def test_bt_to_json_get(self): + """Test case for bt_to_json_get + + Tagesordnungen im JSON-Format abrufen # noqa: E501 + """ + pass + + def test_bt_to_xml_get(self): + """Test case for bt_to_xml_get + + Tagesordnungen im XML-Format abrufen # noqa: E501 + """ + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/python-client/tox.ini b/python-client/tox.ini new file mode 100644 index 0000000..1b05673 --- /dev/null +++ b/python-client/tox.ini @@ -0,0 +1,9 @@ +[tox] +envlist = py3 + +[testenv] +deps=-r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +commands= + pytest --cov=bundestag_tagesordnung