Skip to content

Commit 0c36e79

Browse files
authored
feat: support tenant for create, delete and update on destination module (#59)
1 parent a2ef4a1 commit 0c36e79

File tree

13 files changed

+837
-254
lines changed

13 files changed

+837
-254
lines changed

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.9.0"
3+
version = "0.10.0"
44
description = "SAP Cloud SDK for Python"
55
readme = "README.md"
66
license = "Apache-2.0"

src/sap_cloud_sdk/destination/certificate_client.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,18 @@ def get_subaccount_certificate(
206206

207207
@record_metrics(Module.DESTINATION, Operation.CERTIFICATE_CREATE_CERTIFICATE)
208208
def create_certificate(
209-
self, certificate: Certificate, level: Optional[Level] = Level.SUB_ACCOUNT
209+
self,
210+
certificate: Certificate,
211+
level: Optional[Level] = Level.SUB_ACCOUNT,
212+
tenant: Optional[str] = None,
210213
) -> None:
211214
"""Create a certificate.
212215
213216
Args:
214217
certificate: Certificate entity to create.
215218
level: Scope where the certificate should be created (subaccount by default).
219+
tenant: Subscriber tenant subdomain. When provided, the certificate is created in the
220+
subscriber context; otherwise the provider context is used.
216221
217222
Returns:
218223
None. Success responses from the Destination Service return an empty body.
@@ -225,7 +230,7 @@ def create_certificate(
225230
body = certificate.to_dict()
226231

227232
try:
228-
self._http.post(f"{API_V1}/{coll}", body=body)
233+
self._http.post(f"{API_V1}/{coll}", body=body, tenant_subdomain=tenant)
229234
except HttpError:
230235
raise
231236
except Exception as e:
@@ -235,13 +240,18 @@ def create_certificate(
235240

236241
@record_metrics(Module.DESTINATION, Operation.CERTIFICATE_UPDATE_CERTIFICATE)
237242
def update_certificate(
238-
self, certificate: Certificate, level: Optional[Level] = Level.SUB_ACCOUNT
243+
self,
244+
certificate: Certificate,
245+
level: Optional[Level] = Level.SUB_ACCOUNT,
246+
tenant: Optional[str] = None,
239247
) -> None:
240248
"""Update a certificate.
241249
242250
Args:
243251
certificate: Certificate entity with updated fields.
244252
level: Scope where the certificate exists (subaccount by default).
253+
tenant: Subscriber tenant subdomain. When provided, the certificate is updated in the
254+
subscriber context; otherwise the provider context is used.
245255
246256
Returns:
247257
None. Success responses from the Destination Service return an Update object (e.g., {"Count": 1}),
@@ -255,7 +265,7 @@ def update_certificate(
255265
body = certificate.to_dict()
256266

257267
try:
258-
self._http.put(f"{API_V1}/{coll}", body=body)
268+
self._http.put(f"{API_V1}/{coll}", body=body, tenant_subdomain=tenant)
259269
except HttpError:
260270
raise
261271
except Exception as e:
@@ -265,13 +275,18 @@ def update_certificate(
265275

266276
@record_metrics(Module.DESTINATION, Operation.CERTIFICATE_DELETE_CERTIFICATE)
267277
def delete_certificate(
268-
self, name: str, level: Optional[Level] = Level.SUB_ACCOUNT
278+
self,
279+
name: str,
280+
level: Optional[Level] = Level.SUB_ACCOUNT,
281+
tenant: Optional[str] = None,
269282
) -> None:
270283
"""Delete a certificate.
271284
272285
Args:
273286
name: Certificate name.
274287
level: Scope where the certificate exists (subaccount by default).
288+
tenant: Subscriber tenant subdomain. When provided, the certificate is deleted in the
289+
subscriber context; otherwise the provider context is used.
275290
276291
Raises:
277292
HttpError: Propagated for HTTP errors.
@@ -280,7 +295,7 @@ def delete_certificate(
280295
coll = self._sub_path_for_level(level)
281296

282297
try:
283-
self._http.delete(f"{API_V1}/{coll}/{name}")
298+
self._http.delete(f"{API_V1}/{coll}/{name}", tenant_subdomain=tenant)
284299
except HttpError:
285300
raise
286301
except Exception as e:

src/sap_cloud_sdk/destination/client.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -421,13 +421,18 @@ def get_destination(
421421

422422
@record_metrics(Module.DESTINATION, Operation.DESTINATION_CREATE_DESTINATION)
423423
def create_destination(
424-
self, dest: Destination, level: Optional[Level] = Level.SUB_ACCOUNT
424+
self,
425+
dest: Destination,
426+
level: Optional[Level] = Level.SUB_ACCOUNT,
427+
tenant: Optional[str] = None,
425428
) -> None:
426429
"""Create a destination.
427430
428431
Args:
429432
dest: Destination entity to create.
430433
level: Scope where the destination should be created (subaccount by default).
434+
tenant: Subscriber tenant subdomain. When provided, the destination is created in the
435+
subscriber context; otherwise the provider context is used.
431436
432437
Returns:
433438
None. Success responses from the Destination Service return an empty body.
@@ -440,7 +445,7 @@ def create_destination(
440445
body = dest.to_dict()
441446

442447
try:
443-
self._http.post(f"{API_V1}/{coll}", body=body)
448+
self._http.post(f"{API_V1}/{coll}", body=body, tenant_subdomain=tenant)
444449
except HttpError:
445450
raise
446451
except Exception as e:
@@ -450,13 +455,18 @@ def create_destination(
450455

451456
@record_metrics(Module.DESTINATION, Operation.DESTINATION_UPDATE_DESTINATION)
452457
def update_destination(
453-
self, dest: Destination, level: Optional[Level] = Level.SUB_ACCOUNT
458+
self,
459+
dest: Destination,
460+
level: Optional[Level] = Level.SUB_ACCOUNT,
461+
tenant: Optional[str] = None,
454462
) -> None:
455463
"""Update a destination.
456464
457465
Args:
458466
dest: Destination entity with updated fields.
459467
level: Scope where the destination exists (subaccount by default).
468+
tenant: Subscriber tenant subdomain. When provided, the destination is updated in the
469+
subscriber context; otherwise the provider context is used.
460470
461471
Returns:
462472
None. Success responses from the Destination Service return an Update object (e.g., {"Count": 1}),
@@ -470,7 +480,7 @@ def update_destination(
470480
body = dest.to_dict()
471481

472482
try:
473-
self._http.put(f"{API_V1}/{coll}", body=body)
483+
self._http.put(f"{API_V1}/{coll}", body=body, tenant_subdomain=tenant)
474484
except HttpError:
475485
raise
476486
except Exception as e:
@@ -480,13 +490,18 @@ def update_destination(
480490

481491
@record_metrics(Module.DESTINATION, Operation.DESTINATION_DELETE_DESTINATION)
482492
def delete_destination(
483-
self, name: str, level: Optional[Level] = Level.SUB_ACCOUNT
493+
self,
494+
name: str,
495+
level: Optional[Level] = Level.SUB_ACCOUNT,
496+
tenant: Optional[str] = None,
484497
) -> None:
485498
"""Delete a destination.
486499
487500
Args:
488501
name: Destination name.
489502
level: Scope where the destination exists (subaccount by default).
503+
tenant: Subscriber tenant subdomain. When provided, the destination is deleted in the
504+
subscriber context; otherwise the provider context is used.
490505
491506
Raises:
492507
HttpError: Propagated for HTTP errors.
@@ -495,7 +510,7 @@ def delete_destination(
495510
coll = self._sub_path_for_level(level)
496511

497512
try:
498-
self._http.delete(f"{API_V1}/{coll}/{name}")
513+
self._http.delete(f"{API_V1}/{coll}/{name}", tenant_subdomain=tenant)
499514
except HttpError:
500515
raise
501516
except Exception as e:

src/sap_cloud_sdk/destination/fragment_client.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,18 @@ def list_subaccount_fragments(
191191

192192
@record_metrics(Module.DESTINATION, Operation.FRAGMENT_CREATE_FRAGMENT)
193193
def create_fragment(
194-
self, fragment: Fragment, level: Optional[Level] = Level.SUB_ACCOUNT
194+
self,
195+
fragment: Fragment,
196+
level: Optional[Level] = Level.SUB_ACCOUNT,
197+
tenant: Optional[str] = None,
195198
) -> None:
196199
"""Create a fragment.
197200
198201
Args:
199202
fragment: Fragment entity to create.
200203
level: Scope where the fragment should be created (subaccount by default).
204+
tenant: Subscriber tenant subdomain. When provided, the fragment is created in the
205+
subscriber context; otherwise the provider context is used.
201206
202207
Returns:
203208
None. Success responses from the Destination Service return an empty body.
@@ -210,7 +215,7 @@ def create_fragment(
210215
body = fragment.to_dict()
211216

212217
try:
213-
self._http.post(f"{API_V1}/{coll}", body=body)
218+
self._http.post(f"{API_V1}/{coll}", body=body, tenant_subdomain=tenant)
214219
except HttpError:
215220
raise
216221
except Exception as e:
@@ -220,13 +225,18 @@ def create_fragment(
220225

221226
@record_metrics(Module.DESTINATION, Operation.FRAGMENT_UPDATE_FRAGMENT)
222227
def update_fragment(
223-
self, fragment: Fragment, level: Optional[Level] = Level.SUB_ACCOUNT
228+
self,
229+
fragment: Fragment,
230+
level: Optional[Level] = Level.SUB_ACCOUNT,
231+
tenant: Optional[str] = None,
224232
) -> None:
225233
"""Update a fragment.
226234
227235
Args:
228236
fragment: Fragment entity with updated fields.
229237
level: Scope where the fragment exists (subaccount by default).
238+
tenant: Subscriber tenant subdomain. When provided, the fragment is updated in the
239+
subscriber context; otherwise the provider context is used.
230240
231241
Returns:
232242
None. Success responses from the Destination Service return an Update object (e.g., {"Count": 1}),
@@ -240,7 +250,7 @@ def update_fragment(
240250
body = fragment.to_dict()
241251

242252
try:
243-
self._http.put(f"{API_V1}/{coll}", body=body)
253+
self._http.put(f"{API_V1}/{coll}", body=body, tenant_subdomain=tenant)
244254
except HttpError:
245255
raise
246256
except Exception as e:
@@ -250,13 +260,18 @@ def update_fragment(
250260

251261
@record_metrics(Module.DESTINATION, Operation.FRAGMENT_DELETE_FRAGMENT)
252262
def delete_fragment(
253-
self, name: str, level: Optional[Level] = Level.SUB_ACCOUNT
263+
self,
264+
name: str,
265+
level: Optional[Level] = Level.SUB_ACCOUNT,
266+
tenant: Optional[str] = None,
254267
) -> None:
255268
"""Delete a fragment.
256269
257270
Args:
258271
name: Fragment name.
259272
level: Scope where the fragment exists (subaccount by default).
273+
tenant: Subscriber tenant subdomain. When provided, the fragment is deleted in the
274+
subscriber context; otherwise the provider context is used.
260275
261276
Raises:
262277
HttpError: Propagated for HTTP errors.
@@ -265,7 +280,7 @@ def delete_fragment(
265280
coll = self._sub_path_for_level(level)
266281

267282
try:
268-
self._http.delete(f"{API_V1}/{coll}/{name}")
283+
self._http.delete(f"{API_V1}/{coll}/{name}", tenant_subdomain=tenant)
269284
except HttpError:
270285
raise
271286
except Exception as e:

src/sap_cloud_sdk/destination/user-guide.md

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,41 @@ cert = certificate_client.get_subaccount_certificate("my-cert", access_strategy=
3636
dest = client.get_subaccount_destination("my-destination", access_strategy=AccessStrategy.SUBSCRIBER_FIRST, tenant="tenant-subdomain")
3737
fragment = fragment_client.get_subaccount_fragment("my-fragment", access_strategy=AccessStrategy.SUBSCRIBER_FIRST, tenant="tenant-subdomain")
3838
cert = certificate_client.get_subaccount_certificate("my-cert", access_strategy=AccessStrategy.SUBSCRIBER_FIRST, tenant="tenant-subdomain")
39+
40+
# Fragment write operations with tenant (subscriber context)
41+
new_fragment = Fragment(name="my-fragment", properties={"URL": "https://api.example.com"})
42+
fragment_client.create_fragment(new_fragment, level=Level.SUB_ACCOUNT, tenant="tenant-subdomain")
43+
fragment_client.update_fragment(new_fragment, level=Level.SUB_ACCOUNT, tenant="tenant-subdomain")
44+
fragment_client.delete_fragment("my-fragment", level=Level.SUB_ACCOUNT, tenant="tenant-subdomain")
45+
46+
# Fragment write operations without tenant (provider context)
47+
fragment_client.create_fragment(new_fragment, level=Level.SUB_ACCOUNT)
48+
fragment_client.update_fragment(new_fragment, level=Level.SUB_ACCOUNT)
49+
fragment_client.delete_fragment("my-fragment", level=Level.SUB_ACCOUNT)
50+
51+
# Destination write operations with tenant (subscriber context)
52+
new_dest = Destination(name="my-dest", type="HTTP", url="https://api.example.com")
53+
client.create_destination(new_dest, level=Level.SUB_ACCOUNT, tenant="tenant-subdomain")
54+
client.update_destination(new_dest, level=Level.SUB_ACCOUNT, tenant="tenant-subdomain")
55+
client.delete_destination("my-dest", level=Level.SUB_ACCOUNT, tenant="tenant-subdomain")
56+
57+
# Destination write operations without tenant (provider context)
58+
client.create_destination(new_dest, level=Level.SUB_ACCOUNT)
59+
client.update_destination(new_dest, level=Level.SUB_ACCOUNT)
60+
client.delete_destination("my-dest", level=Level.SUB_ACCOUNT)
61+
62+
# Certificate write operations with tenant (subscriber context)
63+
from sap_cloud_sdk.destination import create_certificate_client
64+
from sap_cloud_sdk.destination._models import Certificate
65+
new_cert = Certificate(name="my-cert.pem", content="base64-encoded-content", type="PEM")
66+
certificate_client.create_certificate(new_cert, level=Level.SUB_ACCOUNT, tenant="tenant-subdomain")
67+
certificate_client.update_certificate(new_cert, level=Level.SUB_ACCOUNT, tenant="tenant-subdomain")
68+
certificate_client.delete_certificate("my-cert.pem", level=Level.SUB_ACCOUNT, tenant="tenant-subdomain")
69+
70+
# Certificate write operations without tenant (provider context)
71+
certificate_client.create_certificate(new_cert, level=Level.SUB_ACCOUNT)
72+
certificate_client.update_certificate(new_cert, level=Level.SUB_ACCOUNT)
73+
certificate_client.delete_certificate("my-cert.pem", level=Level.SUB_ACCOUNT)
3974
```
4075

4176
## Concepts
@@ -65,9 +100,9 @@ class DestinationClient:
65100
def list_subaccount_destinations(self, access_strategy: AccessStrategy = AccessStrategy.SUBSCRIBER_FIRST, tenant: Optional[str] = None, filter: Optional[ListOptions] = None) -> PagedResult[Destination]: ...
66101

67102
# V1 Admin API - Write operations
68-
def create_destination(self, dest: Destination, level: Optional[Level] = Level.SUB_ACCOUNT) -> None: ...
69-
def update_destination(self, dest: Destination, level: Optional[Level] = Level.SUB_ACCOUNT) -> None: ...
70-
def delete_destination(self, name: str, level: Optional[Level] = Level.SUB_ACCOUNT) -> None: ...
103+
def create_destination(self, dest: Destination, level: Optional[Level] = Level.SUB_ACCOUNT, tenant: Optional[str] = None) -> None: ...
104+
def update_destination(self, dest: Destination, level: Optional[Level] = Level.SUB_ACCOUNT, tenant: Optional[str] = None) -> None: ...
105+
def delete_destination(self, name: str, level: Optional[Level] = Level.SUB_ACCOUNT, tenant: Optional[str] = None) -> None: ...
71106

72107
# V2 Runtime API - Destination consumption with automatic token retrieval
73108
def get_destination(self, name: str, level: Optional[Level] = None, options: Optional[ConsumptionOptions] = None, proxy_enabled: Optional[bool] = None) -> Optional[Destination | TransparentProxyDestination]: ...
@@ -83,9 +118,9 @@ class FragmentClient:
83118
def get_subaccount_fragment(self, name: str, access_strategy: AccessStrategy = AccessStrategy.SUBSCRIBER_FIRST, tenant: Optional[str] = None) -> Optional[Fragment]: ...
84119
def list_instance_fragments(self) -> List[Fragment]: ...
85120
def list_subaccount_fragments(self, access_strategy: AccessStrategy = AccessStrategy.SUBSCRIBER_FIRST, tenant: Optional[str] = None) -> List[Fragment]: ...
86-
def create_fragment(self, fragment: Fragment, level: Optional[Level] = Level.SUB_ACCOUNT) -> None: ...
87-
def update_fragment(self, fragment: Fragment, level: Optional[Level] = Level.SUB_ACCOUNT) -> None: ...
88-
def delete_fragment(self, name: str, level: Optional[Level] = Level.SUB_ACCOUNT) -> None: ...
121+
def create_fragment(self, fragment: Fragment, level: Optional[Level] = Level.SUB_ACCOUNT, tenant: Optional[str] = None) -> None: ...
122+
def update_fragment(self, fragment: Fragment, level: Optional[Level] = Level.SUB_ACCOUNT, tenant: Optional[str] = None) -> None: ...
123+
def delete_fragment(self, name: str, level: Optional[Level] = Level.SUB_ACCOUNT, tenant: Optional[str] = None) -> None: ...
89124
```
90125

91126
### Certificate Client
@@ -98,9 +133,9 @@ class CertificateClient:
98133
def get_subaccount_certificate(self, name: str, access_strategy: AccessStrategy = AccessStrategy.SUBSCRIBER_FIRST, tenant: Optional[str] = None) -> Optional[Certificate]: ...
99134
def list_instance_certificates(self, filter: Optional[ListOptions] = None) -> PagedResult[Certificate]: ...
100135
def list_subaccount_certificates(self, access_strategy: AccessStrategy = AccessStrategy.SUBSCRIBER_FIRST, tenant: Optional[str] = None, filter: Optional[ListOptions] = None) -> PagedResult[Certificate]: ...
101-
def create_certificate(self, certificate: Certificate, level: Optional[Level] = Level.SUB_ACCOUNT) -> None: ...
102-
def update_certificate(self, certificate: Certificate, level: Optional[Level] = Level.SUB_ACCOUNT) -> None: ...
103-
def delete_certificate(self, name: str, level: Optional[Level] = Level.SUB_ACCOUNT) -> None: ...
136+
def create_certificate(self, certificate: Certificate, level: Optional[Level] = Level.SUB_ACCOUNT, tenant: Optional[str] = None) -> None: ...
137+
def update_certificate(self, certificate: Certificate, level: Optional[Level] = Level.SUB_ACCOUNT, tenant: Optional[str] = None) -> None: ...
138+
def delete_certificate(self, name: str, level: Optional[Level] = Level.SUB_ACCOUNT, tenant: Optional[str] = None) -> None: ...
104139
```
105140

106141
### Models

0 commit comments

Comments
 (0)