Skip to content

Commit 1ff9fe9

Browse files
committed
add skip token retrieval for getting dest properties
1 parent 8c460aa commit 1ff9fe9

8 files changed

Lines changed: 83 additions & 5 deletions

File tree

src/sap_cloud_sdk/agentgateway/_lob.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ def get_ias_client_id_lob() -> str:
118118
"""Read the IAS client ID from the IAS destination properties (LoB flow).
119119
120120
Fetches the IAS destination (``sap-managed-runtime-ias-{landscape}``)
121-
at provider subaccount level and returns the ``clientId`` property.
121+
at provider subaccount level with ``$skipTokenRetrieval=true`` so only
122+
destination properties are returned — no auth token exchange is performed.
122123
123124
Returns:
124125
The IAS client ID string, or ``""`` if the destination is not found
@@ -133,6 +134,7 @@ def get_ias_client_id_lob() -> str:
133134
dest = client.get_destination(
134135
dest_name,
135136
level=ConsumptionLevel.PROVIDER_SUBACCOUNT,
137+
options=ConsumptionOptions(skip_token_retrieval=True),
136138
)
137139
if not dest:
138140
logger.warning(

src/sap_cloud_sdk/destination/_models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,10 @@ class ConsumptionOptions:
510510
chain_vars: Key-value pairs for destination chain variables (X-chain-var-<name>).
511511
Each entry is sent as a separate "X-chain-var-<key>" header. Only applicable
512512
when chain_name is provided.
513+
skip_token_retrieval: When True, instructs the Destination Service to skip the
514+
OAuth2 token exchange and return only the destination configuration properties
515+
($skipTokenRetrieval query parameter). Useful when only destination metadata
516+
is needed and token retrieval would be wasteful or cause unnecessary errors.
513517
514518
Example:
515519
```python
@@ -560,6 +564,7 @@ class ConsumptionOptions:
560564
code_verifier: Optional[str] = None
561565
chain_name: Optional[str] = None
562566
chain_vars: Optional[dict] = None
567+
skip_token_retrieval: bool = False
563568

564569

565570
@dataclass

src/sap_cloud_sdk/destination/client.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import logging
66
import warnings
7-
from typing import List, Optional, Callable, TypeVar
7+
from typing import Any, Dict, List, Optional, Callable, TypeVar
88

99
from sap_cloud_sdk.core.telemetry import Module, Operation, record_metrics
1010
from sap_cloud_sdk.destination._http import DestinationHttp, API_V1, API_V2
@@ -430,7 +430,11 @@ def get_destination(
430430
else f"{API_V2}/destinations/{name}"
431431
)
432432

433-
resp = self._http.get(path, headers=headers, tenant_subdomain=tenant)
433+
params: Dict[str, Any] = {}
434+
if options and options.skip_token_retrieval:
435+
params["$skipTokenRetrieval"] = "true"
436+
437+
resp = self._http.get(path, headers=headers, tenant_subdomain=tenant, params=params or None)
434438
data = resp.json()
435439

436440
# Parse v2 response: destinationConfiguration + authTokens + certificates

tests/agentgateway/integration/agw_auth.feature

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,7 @@ Feature: Agent Gateway Auth Integration
5555
When I call list_mcp_tools
5656
And I call call_mcp_tool with the sample MCP tool and the user token
5757
Then the tool result should be a non-empty string
58+
59+
Scenario: Get IAS client ID returns a non-empty string
60+
When I call get_ias_client_id
61+
Then the ias_client_id should be a non-empty string

tests/agentgateway/integration/test_agw_bdd.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def __init__(self):
5454
self.tools: Optional[list[MCPTool]] = None
5555
self.tool_result: Optional[str] = None
5656
self.sample_mcp_tool_name: Optional[str] = None
57+
self.ias_client_id: Optional[str] = None
5758

5859

5960
@pytest.fixture
@@ -266,3 +267,17 @@ def tool_result_is_non_empty_string(context: ScenarioContext):
266267
assert context.tool_result is not None
267268
assert isinstance(context.tool_result, str)
268269
assert context.tool_result.strip(), "Expected a non-empty tool result"
270+
271+
272+
@when("I call get_ias_client_id")
273+
def call_get_ias_client_id(context: ScenarioContext, agw_client: AgentGatewayClient):
274+
"""Call get_ias_client_id and store the result."""
275+
context.ias_client_id = agw_client.get_ias_client_id()
276+
277+
278+
@then("the ias_client_id should be a non-empty string")
279+
def ias_client_id_non_empty(context: ScenarioContext):
280+
"""Verify the IAS client ID is a non-empty string."""
281+
assert context.ias_client_id is not None
282+
assert isinstance(context.ias_client_id, str)
283+
assert context.ias_client_id.strip(), "Expected a non-empty IAS client ID"

tests/agentgateway/unit/test_lob.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from sap_cloud_sdk.agentgateway._models import Agent, AgentCard, MCPTool
2929
from sap_cloud_sdk.agentgateway._token_cache import _GatewayUrlCache, _TokenCache
3030
from sap_cloud_sdk.agentgateway.config import ClientConfig
31+
from sap_cloud_sdk.destination import ConsumptionOptions, ConsumptionLevel
3132
from sap_cloud_sdk.agentgateway.exceptions import AgentGatewaySDKError, MCPServerNotFoundError
3233
from sap_cloud_sdk.destination import ConsumptionLevel
3334

@@ -1032,7 +1033,8 @@ def test_returns_client_id_from_destination_properties(self):
10321033
assert result == "lob-client-id"
10331034
mock_dest_client.get_destination.assert_called_once_with(
10341035
"sap-managed-runtime-ias-eu10",
1035-
level=mock_dest_client.get_destination.call_args[1]["level"],
1036+
level=ConsumptionLevel.PROVIDER_SUBACCOUNT,
1037+
options=ConsumptionOptions(skip_token_retrieval=True),
10361038
)
10371039

10381040
def test_returns_empty_string_when_destination_not_found(self):

tests/destination/unit/test_client.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,52 @@ def test_get_destination_default_proxy_disabled(self, mock_load_proxy):
11731173
# HTTP should be called since proxy is disabled by default
11741174
mock_http.get.assert_called_once()
11751175

1176+
def test_get_destination_skip_token_retrieval_sends_query_param(self):
1177+
"""Test that skip_token_retrieval=True sends $skipTokenRetrieval=true query param."""
1178+
mock_http = MagicMock()
1179+
resp = MagicMock(spec=Response)
1180+
resp.status_code = 200
1181+
resp.json.return_value = {
1182+
"destinationConfiguration": {
1183+
"name": "my-api",
1184+
"type": "HTTP",
1185+
"url": "https://api.example.com",
1186+
"clientId": "my-client-id",
1187+
},
1188+
"authTokens": [],
1189+
"certificates": [],
1190+
}
1191+
mock_http.get.return_value = resp
1192+
1193+
client = DestinationClient(mock_http)
1194+
result = client.get_destination(
1195+
"my-api",
1196+
options=ConsumptionOptions(skip_token_retrieval=True),
1197+
)
1198+
1199+
assert isinstance(result, Destination)
1200+
assert result.properties.get("clientId") == "my-client-id"
1201+
_, kwargs = mock_http.get.call_args
1202+
assert kwargs.get("params") == {"$skipTokenRetrieval": "true"}
1203+
1204+
def test_get_destination_no_skip_token_retrieval_by_default(self):
1205+
"""Test that skip_token_retrieval=False (default) sends no $skipTokenRetrieval param."""
1206+
mock_http = MagicMock()
1207+
resp = MagicMock(spec=Response)
1208+
resp.status_code = 200
1209+
resp.json.return_value = {
1210+
"destinationConfiguration": {"name": "my-api", "type": "HTTP", "url": "https://api.example.com"},
1211+
"authTokens": [],
1212+
"certificates": [],
1213+
}
1214+
mock_http.get.return_value = resp
1215+
1216+
client = DestinationClient(mock_http)
1217+
client.get_destination("my-api")
1218+
1219+
_, kwargs = mock_http.get.call_args
1220+
assert kwargs.get("params") is None
1221+
11761222

11771223
class TestDestinationClientWriteOperations:
11781224

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)