Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
755e3c9
feat(dpi_ng): add SAP DPI NG consent service module to sdk
Ashwith-N Jun 24, 2026
d2751d1
chore: bump version to 0.29.0 and update dependencies
Ashwith-N Jun 24, 2026
ec48be9
feat(dpi_ng): add telemetry instrumentation to dpi_ng/consent module
Ashwith-N Jun 24, 2026
86a978d
test(dpi_ng): add telemetry unit tests for consent module operations
Ashwith-N Jun 24, 2026
f1d6619
feat(dpi_ng/consent): add DPI_NG_CONSENT_* env vars for integration t…
Ashwith-N Jun 24, 2026
4748469
docs(dpi_ng/consent): add integration test setup guide to INTEGRATION…
Ashwith-N Jun 24, 2026
41921ab
chore(dpi_ng/consent): align integration test env vars to CLOUD_SDK_C…
Ashwith-N Jun 24, 2026
69a353d
docs(dpi_ng/consent): replace em dashes with hyphens in integration t…
Ashwith-N Jun 24, 2026
ea0c2b1
docs(dpi_ng/consent): rewrite user guide with full API examples
Ashwith-N Jun 24, 2026
130048f
fix(dpi_ng/consent): resolve ty type-check errors in auth and client
Ashwith-N Jun 25, 2026
2bd15b4
docs(dpi_ng/consent): add and improve docstrings across all public APIs
Ashwith-N Jun 25, 2026
11f0756
docs(dpi_ng/consent): align docstrings with DPI service router URL an…
Ashwith-N Jun 26, 2026
16aea2e
test(dpi_ng/consent): use shared base URL env var in integration test…
Ashwith-N Jun 30, 2026
213d2b0
fix(dpi_ng/consent): add x-tenant-id header support for mTLS client c…
Ashwith-N Jul 2, 2026
94d1e73
docs(dpi_ng/consent): document tenant_id and restructure auth section…
Ashwith-N Jul 2, 2026
3be7919
chore(dpi_ng/consent): apply ruff formatting
Ashwith-N Jul 2, 2026
b051a06
fix(dpi_ng/consent): validate tenant_id is only accepted for ClientCe…
Ashwith-N Jul 2, 2026
9fe6df9
docs(dpi_ng/consent): add ConsentSDKConfig parameter table and fix te…
Ashwith-N Jul 2, 2026
ba8d353
docs(dpi_ng/consent): fix CLOUD_SDK_CFG_DPI_NG_DEFAULT_BASE_URL env v…
Ashwith-N Jul 2, 2026
48d4770
chore(dpi_ng/consent): fix consent user-guide
Ashwith-N Jul 2, 2026
0fb939e
fix(dpi_ng/consent): replace URL substring check with exact equality …
Ashwith-N Jul 6, 2026
f7fd96a
fix(dpi_ng/consent): simplify error message formatting in config vali…
Ashwith-N Jul 6, 2026
02b799b
fix(dpi_ng/consent): correct entity schemas, primary keys, OData quer…
Ashwith-N Jul 9, 2026
343c24b
chore: bump version to 0.34.0
Ashwith-N Jul 9, 2026
b9880e3
fix(dpi_ng/consent): align test assertions, method names, and achieve…
Ashwith-N Jul 9, 2026
4dded81
chore(dpi_ng/consent): apply linting and formatting to test files
Ashwith-N Jul 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .env_integration_tests.example
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@ CLOUD_SDK_CFG_DESTINATION_DEFAULT_TENANT_SUBDOMAIN=your-subscriber-tenant-subdom
CLOUD_SDK_CFG_SDM_DEFAULT_URI=https://your-sdm-api-uri-here
CLOUD_SDK_CFG_SDM_DEFAULT_UAA='{"url":"https://your-auth-url","clientid":"your-client-id","clientsecret":"your-client-secret","identityzone":"your-identity-zone"}'

# DPI NG - CONSENT
# Required for all consent integration tests
CLOUD_SDK_CFG_DPI_NG_DEFAULT_BASE_URL=https://your-dpi-ng-service-host

# Pick ONE of the three auth methods below:

# Option A: Static Bearer Token
# CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_BEARER_TOKEN=your-bearer-token-here
# Option B: OAuth 2.0 Client Credentials
CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_TOKEN_URL=https://your-auth-host/oauth/token
CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_CLIENT_ID=your-client-id
CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_CLIENT_SECRET=your-client-secret
# Option C: Mutual TLS (mTLS)
# CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_CERT_FILE=/path/to/client.crt
# CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_KEY_FILE=/path/to/client.key
# CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_CA_FILE=/path/to/ca.crt # optional

# OBJECT STORE
CLOUD_SDK_CFG_OBJECTSTORE_DEFAULT_HOST=your-objectstore-host-here
CLOUD_SDK_CFG_OBJECTSTORE_DEFAULT_ACCESS_KEY_ID=your-access-key-id-here
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## About this project

This SDK provides consistent interfaces for interacting with foundational services such as object storage, destination management, audit logging, data anonymization, telemetry, and secure credential handling.
This SDK provides consistent interfaces for interacting with foundational services such as object storage, destination management, audit logging, data anonymization, data privacy integration, telemetry, and secure credential handling.

The Python SDK offers a clean, type-safe API following Python best practices while maintaining compatibility with the SAP Application Foundation ecosystem.

Expand All @@ -24,6 +24,7 @@ The Python SDK offers a clean, type-safe API following Python best practices whi
- **Secret Resolver**
- **Telemetry & Observability**
- **Data Anonymization Service**
- **Data Privacy Integration Service**
- **Print Service**

## Requirements and Setup
Expand Down Expand Up @@ -80,6 +81,7 @@ Each module has comprehensive usage guides:
- [Telemetry](src/sap_cloud_sdk/core/telemetry/user-guide.md)
- [Print](src/sap_cloud_sdk/print/user-guide.md)
- [Data Anonymization](src/sap_cloud_sdk/core/data_anonymization/user-guide.md)
- [Data Privacy Integration](src/sap_cloud_sdk/core/dpi_ng/consent/user-guide.md)

## Support, Feedback, Contributing

Expand Down
36 changes: 36 additions & 0 deletions docs/INTEGRATION_TESTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,41 @@ CLOUD_SDK_CFG_SDM_DEFAULT_UAA='{"url":"https://your-auth-url","clientid":"your-c

**Note**: The test fixture automatically onboards test repositories (standard and version-enabled) at session start and cleans them up on teardown. No pre-existing repositories are required.

### DPI NG Consent Integration Tests

For DPI NG Consent integration tests, configure the following variables in `.env_integration_tests`:

```bash
# DPI NG Consent Configuration — use the shared base URL (applies to all DPI NG sub-services)
CLOUD_SDK_CFG_DPI_NG_DEFAULT_BASE_URL=https://your-dpi-ng-service-host
```

Authentication is configurable via one of three methods:

**Option A - Static Bearer Token**

```bash
CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_BEARER_TOKEN=your-bearer-token-here
```

**Option B - OAuth 2.0 Client Credentials**

```bash
CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_TOKEN_URL=https://your-auth-host/oauth/token
CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_CLIENT_ID=your-client-id
CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_CLIENT_SECRET=your-client-secret
```

**Option C - Mutual TLS (mTLS)**

```bash
CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_CERT_FILE=/path/to/client.crt
CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_KEY_FILE=/path/to/client.key
CLOUD_SDK_CFG_DPI_NG_CONSENT_DEFAULT_CA_FILE=/path/to/ca.crt # optional
```

Tests are skipped automatically when `CLOUD_SDK_CFG_DPI_NG_DEFAULT_BASE_URL` or all auth variables are missing.

### ObjectStore Integration Tests

For ObjectStore integration tests, configure the following variables in `.env_integration_tests`:
Expand Down Expand Up @@ -168,6 +203,7 @@ uv run pytest tests/core/integration/auditlog -v
uv run pytest tests/core/integration/data_anonymization -v
uv run pytest tests/destination/integration/ -v
uv run pytest tests/dms/integration/ -v
uv run pytest tests/core/integration/dpi_ng/consent/ -v
uv run pytest tests/objectstore/integration/ -v
```

Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "sap-cloud-sdk"
version = "0.33.2"
version = "0.34.0"
description = "SAP Cloud SDK for Python"
readme = "README.md"
license = "Apache-2.0"
Expand All @@ -27,6 +27,7 @@ dependencies = [
"opentelemetry-api>=1.42.1",
"opentelemetry-sdk>=1.42.1",
"mcp>=1.1.0",
"python-odata>=0.7.0",
]

[project.optional-dependencies]
Expand Down Expand Up @@ -60,6 +61,7 @@ dev = [
"langchain-community>=0.3.0",
"litellm>=1.40.0",
"langchain-litellm>=0.6.6",
"responses>=0.25",
]

[tool.pytest.ini_options]
Expand Down
1 change: 1 addition & 0 deletions src/sap_cloud_sdk/core/dpi_ng/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""DPI Next Gen SDK modules."""
184 changes: 184 additions & 0 deletions src/sap_cloud_sdk/core/dpi_ng/consent/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
"""Consent SDK - Python client for the DPI V2 Consent Repository.
Quickstart::
from sap_cloud_sdk.core.dpi_ng.consent import create_client, BearerTokenAuth
with create_client(
base_url="https://api.service.<region>.ngdpi.dpp.cloud.sap",
auth=BearerTokenAuth("<xsuaa-bearer-token>"),
) as client:
consents = client.consents.list_consents(filter="lifecycleStatusCode eq '1'")
client.consents.withdraw_consent(WithdrawConsentRequest(...))
Entity objects returned by service methods are python-odata entity instances.
Access fields as attributes: ``entity.purpose_name``, ``entity.consent_id``, etc.
"""

from sap_cloud_sdk.core.telemetry import Module, Operation, record_metrics

from .auth import (
AuthProvider,
BearerTokenAuth,
ClientCertificateAuth,
ClientCredentialsAuth,
)
from .services import (
ConsentConfigurationService,
ConsentPurposeService,
ConsentRetentionService,
ConsentService,
ConsentTemplateService,
)
from .config import ConsentSDKConfig
from .exceptions import (
AuthenticationError,
AuthorizationError,
ClientCreationError,
ConflictError,
ConsentSDKError,
NotFoundError,
ODataError,
ValidationError,
)
from .dtos import (
CheckConsentExistsResult,
CreateConsentRequest,
WithdrawConsentRequest,
)


class ConsentClient:
"""Top-level SDK client providing access to all Consent Service endpoints.
Access each OData service through its typed attribute:
- ``client.consents`` - consent record creation, deletion, withdrawal, termination, existence check, and reads (consentServices)
- ``client.purposes`` - purpose CRUD and lifecycle (consentPurposeExternalServices)
- ``client.templates`` - template CRUD and lifecycle (consentTemplateExternalServices)
- ``client.retention`` - retention rule CRUD and lifecycle (consentRetentionExternalServices)
- ``client.configuration`` - reference data CRUD (consentConfigurationExternalServices)
"""

def __init__(
self,
config: ConsentSDKConfig,
*,
_telemetry_source: Module | None = None,
) -> None:
"""Initialise all service clients from the given config.
Args:
config: Validated ``ConsentSDKConfig`` containing the base URL and auth strategy.
_telemetry_source: Internal parameter; not for end-user use.
"""
from .client import _ODataClient

self._telemetry_source = _telemetry_source
self._odata = _ODataClient(config)
self.consents: ConsentService = ConsentService(
self._odata, _telemetry_source=_telemetry_source
)
self.purposes: ConsentPurposeService = ConsentPurposeService(
self._odata, _telemetry_source=_telemetry_source
)
self.templates: ConsentTemplateService = ConsentTemplateService(
self._odata, _telemetry_source=_telemetry_source
)
self.retention: ConsentRetentionService = ConsentRetentionService(
self._odata, _telemetry_source=_telemetry_source
)
self.configuration: ConsentConfigurationService = ConsentConfigurationService(
self._odata, _telemetry_source=_telemetry_source
)

def close(self) -> None:
"""Close the underlying OData HTTP session."""
self._odata.close()

def __enter__(self) -> "ConsentClient":
"""Support use as a context manager."""
return self

def __exit__(self, *_: object) -> None:
"""Close the session on context manager exit."""
self.close()


@record_metrics(Module.DPI_NG, Operation.DPI_NG_CONSENT_CREATE_CLIENT)
def create_client(
config: ConsentSDKConfig | None = None,
*,
base_url: str | None = None,
auth: AuthProvider | None = None,
timeout: float = 30.0,
verify_ssl: bool = True,
_telemetry_source: Module | None = None,
) -> ConsentClient:
"""Create a ConsentClient with explicit configuration or individual keyword arguments.
Args:
config: Pre-built ``ConsentSDKConfig``. When provided, all other kwargs
are ignored.
base_url: URL of the DPI external service router
(e.g. ``https://api.service.<region>.ngdpi.dpp.cloud.sap``).
Found in the credentials of the ``data-privacy-integration`` service instance.
Required when *config* is not provided.
auth: Authentication strategy (``BearerTokenAuth``,
``ClientCredentialsAuth``, ``ClientCertificateAuth``, etc.).
Required when *config* is not provided.
timeout: HTTP request timeout in seconds. Defaults to ``30.0``.
verify_ssl: Whether to verify TLS certificates. Defaults to ``True``.
_telemetry_source: Internal parameter; not for end-user use.
Returns:
ConsentClient ready for consent management calls.
Raises:
ClientCreationError: If required fields are missing or client creation fails.
Note:
Telemetry for client creation records only module/operation metadata and
never includes configuration values or processed user content.
"""
try:
if config is None:
if not base_url or not auth:
raise ValueError(
"base_url and auth are required when config is not provided"
)
config = ConsentSDKConfig(
base_url=base_url,
auth=auth,
timeout=timeout,
verify_ssl=verify_ssl,
)
return ConsentClient(config, _telemetry_source=_telemetry_source)
except (ValueError, TypeError) as exc:
raise ClientCreationError(str(exc)) from exc


__all__ = [
# factory + top-level client
"create_client",
"ConsentClient",
"ConsentSDKConfig",
# auth strategies
"AuthProvider",
"BearerTokenAuth",
"ClientCredentialsAuth",
"ClientCertificateAuth",
# exceptions
"ConsentSDKError",
"ClientCreationError",
"AuthenticationError",
"AuthorizationError",
"ValidationError",
"NotFoundError",
"ConflictError",
"ODataError",
# request / response DTOs
"CreateConsentRequest",
"WithdrawConsentRequest",
"CheckConsentExistsResult",
]
Loading
Loading