From 00e1e09a6543daf1bb27545a82b51af3c4fc711d Mon Sep 17 00:00:00 2001 From: SDK Generator Bot Date: Fri, 13 Mar 2026 16:15:04 +0000 Subject: [PATCH 1/2] Generate sfs --- services/sfs/oas_commit | 2 +- services/sfs/src/stackit/sfs/api_client.py | 31 +++++++++++++------ services/sfs/src/stackit/sfs/exceptions.py | 2 +- .../sfs/src/stackit/sfs/models/__init__.py | 1 - .../create_share_export_policy_payload.py | 6 ++-- .../list_performance_classes_response.py | 6 ++-- .../list_resource_pool_snapshots_response.py | 6 ++-- .../models/list_resource_pools_response.py | 6 ++-- .../list_share_export_policies_response.py | 6 ++-- .../sfs/models/list_shares_response.py | 6 ++-- .../stackit/sfs/models/share_export_policy.py | 6 ++-- services/sfs/src/stackit/sfs/models/status.py | 6 ++-- .../update_share_export_policy_payload.py | 6 ++-- .../stackit/sfs/models/validation_error.py | 6 ++-- services/sfs/src/stackit/sfs/rest.py | 22 +++++++++++-- 15 files changed, 73 insertions(+), 45 deletions(-) diff --git a/services/sfs/oas_commit b/services/sfs/oas_commit index 5c20c205..e3713dde 100644 --- a/services/sfs/oas_commit +++ b/services/sfs/oas_commit @@ -1 +1 @@ -fe212a12ec79a23b81cb53d9a7728f5706bddc23 +0e64886dd0847341800d7191ed193b75413be998 diff --git a/services/sfs/src/stackit/sfs/api_client.py b/services/sfs/src/stackit/sfs/api_client.py index d257a64b..7fb86146 100644 --- a/services/sfs/src/stackit/sfs/api_client.py +++ b/services/sfs/src/stackit/sfs/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/sfs/src/stackit/sfs/exceptions.py b/services/sfs/src/stackit/sfs/exceptions.py index e87acd73..08598cbf 100644 --- a/services/sfs/src/stackit/sfs/exceptions.py +++ b/services/sfs/src/stackit/sfs/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/sfs/src/stackit/sfs/models/__init__.py b/services/sfs/src/stackit/sfs/models/__init__.py index 9afc45cb..444e6fde 100644 --- a/services/sfs/src/stackit/sfs/models/__init__.py +++ b/services/sfs/src/stackit/sfs/models/__init__.py @@ -12,7 +12,6 @@ Do not edit the class manually. """ # noqa: E501 - # import models into model package from stackit.sfs.models.create_resource_pool_payload import CreateResourcePoolPayload from stackit.sfs.models.create_resource_pool_response import CreateResourcePoolResponse diff --git a/services/sfs/src/stackit/sfs/models/create_share_export_policy_payload.py b/services/sfs/src/stackit/sfs/models/create_share_export_policy_payload.py index 4f795610..ee63de25 100644 --- a/services/sfs/src/stackit/sfs/models/create_share_export_policy_payload.py +++ b/services/sfs/src/stackit/sfs/models/create_share_export_policy_payload.py @@ -81,9 +81,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in rules (list) _items = [] if self.rules: - for _item in self.rules: - if _item: - _items.append(_item.to_dict()) + for _item_rules in self.rules: + if _item_rules: + _items.append(_item_rules.to_dict()) _dict["rules"] = _items return _dict diff --git a/services/sfs/src/stackit/sfs/models/list_performance_classes_response.py b/services/sfs/src/stackit/sfs/models/list_performance_classes_response.py index 9cb93fdc..0fd72e37 100644 --- a/services/sfs/src/stackit/sfs/models/list_performance_classes_response.py +++ b/services/sfs/src/stackit/sfs/models/list_performance_classes_response.py @@ -73,9 +73,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in performance_classes (list) _items = [] if self.performance_classes: - for _item in self.performance_classes: - if _item: - _items.append(_item.to_dict()) + for _item_performance_classes in self.performance_classes: + if _item_performance_classes: + _items.append(_item_performance_classes.to_dict()) _dict["performanceClasses"] = _items return _dict diff --git a/services/sfs/src/stackit/sfs/models/list_resource_pool_snapshots_response.py b/services/sfs/src/stackit/sfs/models/list_resource_pool_snapshots_response.py index 05644493..e7354a76 100644 --- a/services/sfs/src/stackit/sfs/models/list_resource_pool_snapshots_response.py +++ b/services/sfs/src/stackit/sfs/models/list_resource_pool_snapshots_response.py @@ -73,9 +73,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in resource_pool_snapshots (list) _items = [] if self.resource_pool_snapshots: - for _item in self.resource_pool_snapshots: - if _item: - _items.append(_item.to_dict()) + for _item_resource_pool_snapshots in self.resource_pool_snapshots: + if _item_resource_pool_snapshots: + _items.append(_item_resource_pool_snapshots.to_dict()) _dict["resourcePoolSnapshots"] = _items return _dict diff --git a/services/sfs/src/stackit/sfs/models/list_resource_pools_response.py b/services/sfs/src/stackit/sfs/models/list_resource_pools_response.py index 8300e9e8..df4f7025 100644 --- a/services/sfs/src/stackit/sfs/models/list_resource_pools_response.py +++ b/services/sfs/src/stackit/sfs/models/list_resource_pools_response.py @@ -73,9 +73,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in resource_pools (list) _items = [] if self.resource_pools: - for _item in self.resource_pools: - if _item: - _items.append(_item.to_dict()) + for _item_resource_pools in self.resource_pools: + if _item_resource_pools: + _items.append(_item_resource_pools.to_dict()) _dict["resourcePools"] = _items return _dict diff --git a/services/sfs/src/stackit/sfs/models/list_share_export_policies_response.py b/services/sfs/src/stackit/sfs/models/list_share_export_policies_response.py index 797e7f54..cf1f04e0 100644 --- a/services/sfs/src/stackit/sfs/models/list_share_export_policies_response.py +++ b/services/sfs/src/stackit/sfs/models/list_share_export_policies_response.py @@ -73,9 +73,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in share_export_policies (list) _items = [] if self.share_export_policies: - for _item in self.share_export_policies: - if _item: - _items.append(_item.to_dict()) + for _item_share_export_policies in self.share_export_policies: + if _item_share_export_policies: + _items.append(_item_share_export_policies.to_dict()) _dict["shareExportPolicies"] = _items return _dict diff --git a/services/sfs/src/stackit/sfs/models/list_shares_response.py b/services/sfs/src/stackit/sfs/models/list_shares_response.py index 958716e6..ec470338 100644 --- a/services/sfs/src/stackit/sfs/models/list_shares_response.py +++ b/services/sfs/src/stackit/sfs/models/list_shares_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 shares (list) _items = [] if self.shares: - for _item in self.shares: - if _item: - _items.append(_item.to_dict()) + for _item_shares in self.shares: + if _item_shares: + _items.append(_item_shares.to_dict()) _dict["shares"] = _items return _dict diff --git a/services/sfs/src/stackit/sfs/models/share_export_policy.py b/services/sfs/src/stackit/sfs/models/share_export_policy.py index 32fa3661..36863e58 100644 --- a/services/sfs/src/stackit/sfs/models/share_export_policy.py +++ b/services/sfs/src/stackit/sfs/models/share_export_policy.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 rules (list) _items = [] if self.rules: - for _item in self.rules: - if _item: - _items.append(_item.to_dict()) + for _item_rules in self.rules: + if _item_rules: + _items.append(_item_rules.to_dict()) _dict["rules"] = _items return _dict diff --git a/services/sfs/src/stackit/sfs/models/status.py b/services/sfs/src/stackit/sfs/models/status.py index 512300c4..98da8f1b 100644 --- a/services/sfs/src/stackit/sfs/models/status.py +++ b/services/sfs/src/stackit/sfs/models/status.py @@ -82,9 +82,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in details (list) _items = [] if self.details: - for _item in self.details: - if _item: - _items.append(_item.to_dict()) + for _item_details in self.details: + if _item_details: + _items.append(_item_details.to_dict()) _dict["details"] = _items return _dict diff --git a/services/sfs/src/stackit/sfs/models/update_share_export_policy_payload.py b/services/sfs/src/stackit/sfs/models/update_share_export_policy_payload.py index b201be55..a00e8086 100644 --- a/services/sfs/src/stackit/sfs/models/update_share_export_policy_payload.py +++ b/services/sfs/src/stackit/sfs/models/update_share_export_policy_payload.py @@ -80,9 +80,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in rules (list) _items = [] if self.rules: - for _item in self.rules: - if _item: - _items.append(_item.to_dict()) + for _item_rules in self.rules: + if _item_rules: + _items.append(_item_rules.to_dict()) _dict["rules"] = _items # set to None if rules (nullable) is None # and model_fields_set contains the field diff --git a/services/sfs/src/stackit/sfs/models/validation_error.py b/services/sfs/src/stackit/sfs/models/validation_error.py index 84496136..00af91d0 100644 --- a/services/sfs/src/stackit/sfs/models/validation_error.py +++ b/services/sfs/src/stackit/sfs/models/validation_error.py @@ -78,9 +78,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of each item in fields (list) _items = [] if self.fields: - for _item in self.fields: - if _item: - _items.append(_item.to_dict()) + for _item_fields in self.fields: + if _item_fields: + _items.append(_item_fields.to_dict()) _dict["fields"] = _items # set to None if error_description (nullable) is None # and model_fields_set contains the field diff --git a/services/sfs/src/stackit/sfs/rest.py b/services/sfs/src/stackit/sfs/rest.py index 731213af..db4c31ce 100644 --- a/services/sfs/src/stackit/sfs/rest.py +++ b/services/sfs/src/stackit/sfs/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)]) From 74702266ba0771eae97c8287a602e2ecb0dddad9 Mon Sep 17 00:00:00 2001 From: Carlo Goetz Date: Tue, 17 Mar 2026 13:28:13 +0100 Subject: [PATCH 2/2] chore(sfs) write changelog, bump version --- CHANGELOG.md | 8 ++++++-- services/sfs/CHANGELOG.md | 4 ++++ services/sfs/pyproject.toml | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1921373c..4bb28e38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,8 +91,12 @@ - `scf`: [v0.4.1](services/scf/CHANGELOG.md#v041) - **Feature:** client now supports UUID and decimal types - **Bugfix:** timeouts now passed to requests library -- `sfs`: [v0.3.0](services/sfs/CHANGELOG.md#v030) - - **Breaking change:** The `name` and `spaceHardLimitGigabytes` fields are now marked as required for `ShareExportPayload`, `SharePayload`. +- `sfs`: + - [v0.3.1](services/sfs/CHANGELOG.md#v031) + - **Feature:** client now supports UUID and decimal types + - **Bugfix:** timeouts now passed to requests library + - [v0.3.0](services/sfs/CHANGELOG.md#v030) + - **Breaking change:** The `name` and `spaceHardLimitGigabytes` fields are now marked as required for `ShareExportPayload`, `SharePayload`. - `secretsmanager`: [v0.4.1](services/secretsmanager/CHANGELOG.md#v041) - **Feature:** client now supports UUID and decimal types - **Bugfix:** timeouts now passed to requests library diff --git a/services/sfs/CHANGELOG.md b/services/sfs/CHANGELOG.md index 01fb34fa..971e62a7 100644 --- a/services/sfs/CHANGELOG.md +++ b/services/sfs/CHANGELOG.md @@ -1,3 +1,7 @@ +## v0.3.1 +- **Feature:** client now supports UUID and decimal types +- **Bugfix:** timeouts now passed to requests library + ## v0.3.0 - **Breaking change:** The `name` and `spaceHardLimitGigabytes` fields are now marked as required for `ShareExportPayload`, `SharePayload`. diff --git a/services/sfs/pyproject.toml b/services/sfs/pyproject.toml index 7a175774..1484fbae 100644 --- a/services/sfs/pyproject.toml +++ b/services/sfs/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "stackit-sfs" -version = "v0.3.0" +version = "v0.3.1" description = "STACKIT File Storage (SFS)" authors = [{ name = "STACKIT Developer Tools", email = "developer-tools@stackit.cloud" }] requires-python = ">=3.9,<4"