diff --git a/CHANGELOG.md b/CHANGELOG.md index cfc3bde7..44965a37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,9 @@ - **Breaking change:** Renamed `TargetPoolTlsConfig` to `TlsConfig` - `loadbalancer`: [v0.9.0](services/loadbalancer/CHANGELOG.md#v090) - **Feature:** Add new fields `AltPort` and `HttpHealthCheck` to `ActiveHealthCheck` +- `rabbitmq`: [v0.4.1](services/rabbitmq/CHANGELOG.md#v041) + - **Feature:** client now supports UUID and decimal types + - **Bugfix:** timeouts now passed to requests library - `redis`: [v0.3.1](services/redis/CHANGELOG.md#v031) - **Feature:** client now supports UUID and decimal types - **Bugfix:** timeouts now passed to requests library diff --git a/services/rabbitmq/CHANGELOG.md b/services/rabbitmq/CHANGELOG.md index 241ed453..bb9459d2 100644 --- a/services/rabbitmq/CHANGELOG.md +++ b/services/rabbitmq/CHANGELOG.md @@ -1,3 +1,7 @@ +## v0.4.1 +- **Feature:** client now supports UUID and decimal types +- **Bugfix:** timeouts now passed to requests library + ## v0.4.0 - **Breaking Change:** `tls_protocols` on `instance_parameters` is now a list of strings instead of a single string diff --git a/services/rabbitmq/oas_commit b/services/rabbitmq/oas_commit index 064e1081..e3713dde 100644 --- a/services/rabbitmq/oas_commit +++ b/services/rabbitmq/oas_commit @@ -1 +1 @@ -cfcfc32bc1ae335acecd7bf7ffb60354fdc565ab +0e64886dd0847341800d7191ed193b75413be998 diff --git a/services/rabbitmq/pyproject.toml b/services/rabbitmq/pyproject.toml index 29b1a28e..e81720ef 100644 --- a/services/rabbitmq/pyproject.toml +++ b/services/rabbitmq/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "stackit-rabbitmq" -version = "v0.4.0" +version = "v0.4.1" description = "STACKIT RabbitMQ API" authors = [{ name = "STACKIT Developer Tools", email = "developer-tools@stackit.cloud" }] requires-python = ">=3.9,<4.0" diff --git a/services/rabbitmq/src/stackit/rabbitmq/api_client.py b/services/rabbitmq/src/stackit/rabbitmq/api_client.py index 61ee6f3c..8dbb39e7 100644 --- a/services/rabbitmq/src/stackit/rabbitmq/api_client.py +++ b/services/rabbitmq/src/stackit/rabbitmq/api_client.py @@ -12,11 +12,13 @@ """ # noqa: E501 import datetime +import decimal import json import mimetypes import os import re import tempfile +import uuid from enum import Enum from typing import Dict, List, Optional, Tuple, Union from urllib.parse import quote @@ -63,8 +65,10 @@ class ApiClient: "bool": bool, "date": datetime.date, "datetime": datetime.datetime, + "decimal": decimal.Decimal, "object": object, } + _pool = None def __init__(self, configuration, header_name=None, header_value=None, cookie=None) -> None: self.config: Configuration = configuration @@ -267,7 +271,7 @@ def response_deserialize( return_data = self.__deserialize_file(response_data) elif response_type is not None: match = None - content_type = response_data.getheader("content-type") + content_type = response_data.headers.get("content-type") if content_type is not None: match = re.search(r"charset=([a-zA-Z\-\d]+)[\s;]?", content_type) encoding = match.group(1) if match else "utf-8" @@ -284,7 +288,7 @@ def response_deserialize( return ApiResponse( status_code=response_data.status, data=return_data, - headers=response_data.getheaders(), + headers=response_data.headers, raw_data=response_data.data, ) @@ -296,6 +300,7 @@ def sanitize_for_serialization(self, obj): 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 decimal.Decimal return string representation. 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. @@ -311,12 +316,16 @@ def sanitize_for_serialization(self, obj): return obj.get_secret_value() elif isinstance(obj, self.PRIMITIVE_TYPES): return obj + elif isinstance(obj, uuid.UUID): + return str(obj) elif isinstance(obj, list): return [self.sanitize_for_serialization(sub_obj) for sub_obj in obj] elif isinstance(obj, tuple): return tuple(self.sanitize_for_serialization(sub_obj) for sub_obj in obj) elif isinstance(obj, (datetime.datetime, datetime.date)): return obj.isoformat() + elif isinstance(obj, decimal.Decimal): + return str(obj) elif isinstance(obj, dict): obj_dict = obj @@ -326,7 +335,7 @@ def sanitize_for_serialization(self, obj): # and attributes which value is not None. # Convert attribute name to json key in # model definition for request. - if hasattr(obj, "to_dict") and callable(obj.to_dict): + if hasattr(obj, "to_dict") and callable(getattr(obj, "to_dict")): # noqa: B009 obj_dict = obj.to_dict() else: obj_dict = obj.__dict__ @@ -354,7 +363,7 @@ def deserialize(self, response_text: str, response_type: str, content_type: Opti data = json.loads(response_text) except ValueError: data = response_text - elif re.match(r"^application/(json|[\w!#$&.+-^_]+\+json)\s*(;|$)", content_type, re.IGNORECASE): + elif re.match(r"^application/(json|[\w!#$&.+\-^_]+\+json)\s*(;|$)", content_type, re.IGNORECASE): if response_text == "": data = "" else: @@ -400,12 +409,14 @@ def __deserialize(self, data, klass): if klass in self.PRIMITIVE_TYPES: return self.__deserialize_primitive(data, klass) - elif klass == object: + elif klass is object: return self.__deserialize_object(data) - elif klass == datetime.date: + elif klass is datetime.date: return self.__deserialize_date(data) - elif klass == datetime.datetime: + elif klass is datetime.datetime: return self.__deserialize_datetime(data) + elif klass is decimal.Decimal: + return decimal.Decimal(data) elif issubclass(klass, Enum): return self.__deserialize_enum(data, klass) else: @@ -553,12 +564,14 @@ def __deserialize_file(self, response): os.close(fd) os.remove(path) - content_disposition = response.getheader("Content-Disposition") + content_disposition = response.headers.get("Content-Disposition") if content_disposition: m = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', content_disposition) if m is None: raise ValueError("Unexpected 'content-disposition' header value") - filename = m.group(1) + filename = os.path.basename(m.group(1)) # Strip any directory traversal + if filename in ("", ".", ".."): # fall back to tmp filename + filename = os.path.basename(path) path = os.path.join(os.path.dirname(path), filename) with open(path, "wb") as f: diff --git a/services/rabbitmq/src/stackit/rabbitmq/exceptions.py b/services/rabbitmq/src/stackit/rabbitmq/exceptions.py index 0206e5ad..a7e86c2d 100644 --- a/services/rabbitmq/src/stackit/rabbitmq/exceptions.py +++ b/services/rabbitmq/src/stackit/rabbitmq/exceptions.py @@ -129,7 +129,7 @@ def __init__( self.body = http_resp.data.decode("utf-8") except Exception: # noqa: S110 pass - self.headers = http_resp.getheaders() + self.headers = http_resp.headers @classmethod def from_response( diff --git a/services/rabbitmq/src/stackit/rabbitmq/models/__init__.py b/services/rabbitmq/src/stackit/rabbitmq/models/__init__.py index a6129fdc..a8b1c4c8 100644 --- a/services/rabbitmq/src/stackit/rabbitmq/models/__init__.py +++ b/services/rabbitmq/src/stackit/rabbitmq/models/__init__.py @@ -12,7 +12,6 @@ Do not edit the class manually. """ # noqa: E501 - # import models into model package from stackit.rabbitmq.models.backup import Backup from stackit.rabbitmq.models.create_backup_response_item import CreateBackupResponseItem diff --git a/services/rabbitmq/src/stackit/rabbitmq/models/list_backups_response.py b/services/rabbitmq/src/stackit/rabbitmq/models/list_backups_response.py index 18e24ac6..41401a16 100644 --- a/services/rabbitmq/src/stackit/rabbitmq/models/list_backups_response.py +++ b/services/rabbitmq/src/stackit/rabbitmq/models/list_backups_response.py @@ -71,9 +71,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in instance_backups (list) _items = [] if self.instance_backups: - for _item in self.instance_backups: - if _item: - _items.append(_item.to_dict()) + for _item_instance_backups in self.instance_backups: + if _item_instance_backups: + _items.append(_item_instance_backups.to_dict()) _dict["instanceBackups"] = _items return _dict diff --git a/services/rabbitmq/src/stackit/rabbitmq/models/list_credentials_response.py b/services/rabbitmq/src/stackit/rabbitmq/models/list_credentials_response.py index e4402218..93f32203 100644 --- a/services/rabbitmq/src/stackit/rabbitmq/models/list_credentials_response.py +++ b/services/rabbitmq/src/stackit/rabbitmq/models/list_credentials_response.py @@ -71,9 +71,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in credentials_list (list) _items = [] if self.credentials_list: - for _item in self.credentials_list: - if _item: - _items.append(_item.to_dict()) + for _item_credentials_list in self.credentials_list: + if _item_credentials_list: + _items.append(_item_credentials_list.to_dict()) _dict["credentialsList"] = _items return _dict diff --git a/services/rabbitmq/src/stackit/rabbitmq/models/list_instances_response.py b/services/rabbitmq/src/stackit/rabbitmq/models/list_instances_response.py index 088ca46a..9d787942 100644 --- a/services/rabbitmq/src/stackit/rabbitmq/models/list_instances_response.py +++ b/services/rabbitmq/src/stackit/rabbitmq/models/list_instances_response.py @@ -71,9 +71,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in instances (list) _items = [] if self.instances: - for _item in self.instances: - if _item: - _items.append(_item.to_dict()) + for _item_instances in self.instances: + if _item_instances: + _items.append(_item_instances.to_dict()) _dict["instances"] = _items return _dict diff --git a/services/rabbitmq/src/stackit/rabbitmq/models/list_offerings_response.py b/services/rabbitmq/src/stackit/rabbitmq/models/list_offerings_response.py index 4cc8aab9..16451aea 100644 --- a/services/rabbitmq/src/stackit/rabbitmq/models/list_offerings_response.py +++ b/services/rabbitmq/src/stackit/rabbitmq/models/list_offerings_response.py @@ -71,9 +71,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in offerings (list) _items = [] if self.offerings: - for _item in self.offerings: - if _item: - _items.append(_item.to_dict()) + for _item_offerings in self.offerings: + if _item_offerings: + _items.append(_item_offerings.to_dict()) _dict["offerings"] = _items return _dict diff --git a/services/rabbitmq/src/stackit/rabbitmq/models/list_restores_response.py b/services/rabbitmq/src/stackit/rabbitmq/models/list_restores_response.py index 08ec6718..4c8b53c8 100644 --- a/services/rabbitmq/src/stackit/rabbitmq/models/list_restores_response.py +++ b/services/rabbitmq/src/stackit/rabbitmq/models/list_restores_response.py @@ -71,9 +71,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in instance_restores (list) _items = [] if self.instance_restores: - for _item in self.instance_restores: - if _item: - _items.append(_item.to_dict()) + for _item_instance_restores in self.instance_restores: + if _item_instance_restores: + _items.append(_item_instance_restores.to_dict()) _dict["instanceRestores"] = _items return _dict diff --git a/services/rabbitmq/src/stackit/rabbitmq/models/offering.py b/services/rabbitmq/src/stackit/rabbitmq/models/offering.py index 1bb0fe4c..cce0c440 100644 --- a/services/rabbitmq/src/stackit/rabbitmq/models/offering.py +++ b/services/rabbitmq/src/stackit/rabbitmq/models/offering.py @@ -99,9 +99,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in plans (list) _items = [] if self.plans: - for _item in self.plans: - if _item: - _items.append(_item.to_dict()) + for _item_plans in self.plans: + if _item_plans: + _items.append(_item_plans.to_dict()) _dict["plans"] = _items # override the default output from pydantic by calling `to_dict()` of var_schema if self.var_schema: diff --git a/services/rabbitmq/src/stackit/rabbitmq/rest.py b/services/rabbitmq/src/stackit/rabbitmq/rest.py index bcefa6ab..628e3abc 100644 --- a/services/rabbitmq/src/stackit/rabbitmq/rest.py +++ b/services/rabbitmq/src/stackit/rabbitmq/rest.py @@ -38,12 +38,17 @@ def read(self): self.data = self.response.content return self.data + @property + def headers(self): + """Returns a dictionary of response headers.""" + return self.response.headers + def getheaders(self): - """Returns a dictionary of the response headers.""" + """Returns a dictionary of the response headers; use ``headers`` instead.""" return self.response.headers def getheader(self, name, default=None): - """Returns a given response header.""" + """Returns a given response header; use ``headers.get()`` instead.""" return self.response.headers.get(name, default) @@ -93,6 +98,7 @@ def request(self, method, url, headers=None, body=None, post_params=None, _reque url, data=request_body, headers=headers, + timeout=_request_timeout, ) elif content_type == "application/x-www-form-urlencoded": r = self.session.request( @@ -100,6 +106,7 @@ def request(self, method, url, headers=None, body=None, post_params=None, _reque url, params=post_params, headers=headers, + timeout=_request_timeout, ) elif content_type == "multipart/form-data": # must del headers['Content-Type'], or the correct @@ -113,6 +120,7 @@ def request(self, method, url, headers=None, body=None, post_params=None, _reque url, files=post_params, headers=headers, + timeout=_request_timeout, ) # Pass a `string` parameter directly in the body to support # other content types than JSON when `body` argument is @@ -123,10 +131,17 @@ def request(self, method, url, headers=None, body=None, post_params=None, _reque url, data=body, headers=headers, + timeout=_request_timeout, ) elif headers["Content-Type"].startswith("text/") and isinstance(body, bool): request_body = "true" if body else "false" - r = self.session.request(method, url, data=request_body, headers=headers) + r = self.session.request( + method, + url, + data=request_body, + headers=headers, + timeout=_request_timeout, + ) else: # Cannot generate the request from given parameters msg = """Cannot prepare a request message for provided @@ -140,6 +155,7 @@ def request(self, method, url, headers=None, body=None, post_params=None, _reque url, params={}, headers=headers, + timeout=_request_timeout, ) except requests.exceptions.SSLError as e: msg = "\n".join([type(e).__name__, str(e)])