Skip to content

Commit b31dbde

Browse files
committed
fix(ci): bump version to 0.58.2, fix ruff format and ty type suppression
- Bump version 0.58.1 → 0.58.2 (patch; src/ modified on this branch) - Collapse multiline raise in _validation.py to satisfy ruff-format - Add ty: ignore[invalid-argument-type] suppression on intentional non-string test inputs in test_validation.py - Add resource name validation section and @Level shorthand docs to destination user-guide.md
1 parent 1135228 commit b31dbde

4 files changed

Lines changed: 51 additions & 6 deletions

File tree

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "sap-cloud-sdk"
3-
version = "0.58.1"
3+
version = "0.58.2"
44
description = "SAP Cloud SDK for Python"
55
readme = "README.md"
66
license = "Apache-2.0"

‎src/sap_cloud_sdk/destination/user-guide.md‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@ client.patch_destination_labels(
162162
- SUBACCOUNT: Subscriber subaccount scope
163163
- INSTANCE: Subscriber service instance scope
164164

165+
**Shorthand syntax:** `get_destination` accepts the `ConsumptionLevel` value as an `@`-suffix
166+
directly in the name string for backward compatibility
167+
(`"my-dest@provider_subaccount"` is equivalent to passing `level=ConsumptionLevel.PROVIDER_SUBACCOUNT`).
168+
The `level=` parameter form is the canonical and more explicit usage.
169+
165170
- AccessStrategy (applies to subaccount reads):
166171
- SUBSCRIBER_ONLY: Only subscriber (tenant required)
167172
- PROVIDER_ONLY: Only provider (no tenant)
@@ -925,6 +930,48 @@ Entries with a `"tenant"` field are treated as subscriber-specific. Entries with
925930
- `HttpError`: HTTP-related or local store read/write errors with `status_code` and `response_text` when applicable
926931
- `DestinationCertificateError`: raised when a client certificate cannot be loaded or wired into the HTTP session (unsupported format, wrong/missing KeyStorePassword, malformed content, cert/key mismatch)
927932

933+
## Resource Name Validation
934+
935+
All public methods that accept a resource `name` parameter validate it against the following grammar before any OAuth token is fetched or HTTP request is sent:
936+
937+
```
938+
[A-Za-z0-9][A-Za-z0-9._\-]{0,199}
939+
```
940+
941+
- The first character must be alphanumeric (prevents `.` / `..` path segments).
942+
- The remaining characters may be letters, digits, dot (`.`), underscore (`_`), or hyphen (`-`).
943+
- Maximum length: 200 characters.
944+
945+
A `ValueError` is raised immediately for names that do not match — no network call is made:
946+
947+
```python
948+
# Valid names — all accepted
949+
client.get_destination("my-destination")
950+
client.get_destination("MyDest.v2")
951+
client.get_destination("AuditLogV3_Destination")
952+
953+
# Invalid names — ValueError raised before any HTTP call
954+
client.get_destination("../etc/passwd") # path traversal
955+
client.get_destination("a/b") # path separator
956+
client.get_destination("a%2Fb") # encoded separator
957+
client.get_destination("a?query=1") # query delimiter
958+
client.get_destination("") # empty
959+
```
960+
961+
### `get_destination` — ConsumptionLevel shorthand
962+
963+
`get_destination` additionally accepts a `@ConsumptionLevel` suffix embedded in the name for backward compatibility:
964+
965+
```python
966+
# Shorthand — still works, produces identical URL
967+
client.get_destination("my-dest@provider_subaccount")
968+
969+
# Canonical form — explicit and preferred
970+
client.get_destination("my-dest", level=ConsumptionLevel.PROVIDER_SUBACCOUNT)
971+
```
972+
973+
The SDK splits on `@`, validates the base name, and resolves the level automatically. If both the suffix and the `level=` parameter are provided with conflicting values, a `ValueError` is raised.
974+
928975
## Configuration
929976

930977
### Service Binding

‎src/sap_cloud_sdk/destination/utils/_validation.py‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,7 @@ def validate_resource_name(name: str) -> str:
5050
ValueError: If *name* does not match the allowlist grammar.
5151
"""
5252
if not isinstance(name, str) or not _RESOURCE_NAME_RE.fullmatch(name):
53-
raise ValueError(
54-
f"Invalid resource name {name!r}: must match {_NAME_PATTERN}"
55-
)
53+
raise ValueError(f"Invalid resource name {name!r}: must match {_NAME_PATTERN}")
5654
return name
5755

5856

‎tests/destination/unit/test_validation.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ def test_rejects_invalid_names(self, name: str):
6363

6464
def test_rejects_non_string_input(self):
6565
with pytest.raises(ValueError):
66-
validate_resource_name(None) # type: ignore[arg-type]
66+
validate_resource_name(None) # type: ignore[arg-type] # ty: ignore[invalid-argument-type]
6767

6868
def test_rejects_integer_input(self):
6969
with pytest.raises(ValueError):
70-
validate_resource_name(42) # type: ignore[arg-type]
70+
validate_resource_name(42) # type: ignore[arg-type] # ty: ignore[invalid-argument-type]
7171

7272
def test_error_message_includes_name(self):
7373
with pytest.raises(ValueError, match="Invalid resource name"):

0 commit comments

Comments
 (0)