Skip to content

Commit e150a42

Browse files
authored
Fix getting secret keys for connections of type "Custom Keys" (#42937)
1 parent f6ac36d commit e150a42

File tree

9 files changed

+82
-21
lines changed

9 files changed

+82
-21
lines changed

sdk/ai/azure-ai-projects/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Release History
22

3+
## 1.1.0b4 (2025-09-12)
4+
5+
### Bugs Fixed
6+
7+
* Fix getting secret keys for connections of type "Custom Keys" ([GitHub issue 52355](https://github.com/Azure/azure-sdk-for-net/issues/52355))
8+
39
## 1.1.0b3 (2025-08-26)
410

511
### Features added

sdk/ai/azure-ai-projects/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ The client library uses version `2025-05-15-preview` of the AI Foundry [data pla
1515

1616
[Product documentation](https://aka.ms/azsdk/azure-ai-projects/product-doc)
1717
| [Samples][samples]
18-
| [API reference documentation](https://aka.ms/azsdk/azure-ai-projects/python/reference)
18+
| [API reference](https://aka.ms/azsdk/azure-ai-projects/python/reference)
1919
| [Package (PyPI)](https://aka.ms/azsdk/azure-ai-projects/python/package)
2020
| [SDK source code](https://aka.ms/azsdk/azure-ai-projects/python/code)
21+
| [Release history](https://aka.ms/azsdk/azure-ai-projects/python/release-history)
2122

2223
## Reporting issues
2324

sdk/ai/azure-ai-projects/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/ai/azure-ai-projects",
5-
"Tag": "python/ai/azure-ai-projects_f7878e759e"
5+
"Tag": "python/ai/azure-ai-projects_e1ef59ae5f"
66
}

sdk/ai/azure-ai-projects/azure/ai/projects/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
77
# --------------------------------------------------------------------------
88

9-
VERSION = "1.1.0b3"
9+
VERSION = "1.1.0b4"

sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_connections_async.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,16 @@ async def get(self, name: str, *, include_credentials: Optional[bool] = False, *
3939
"""
4040

4141
if include_credentials:
42-
return await super()._get_with_credentials(name, **kwargs)
42+
connection = await super()._get_with_credentials(name, **kwargs)
43+
if connection.type == ConnectionType.CUSTOM:
44+
# Why do we do this? See comment in the sync version of this code (file _patch_connections.py).
45+
setattr(
46+
connection.credentials,
47+
"credential_keys",
48+
{k: v for k, v in connection.credentials.as_dict().items() if k != "type"},
49+
)
50+
51+
return connection
4352

4453
return await super()._get(name, **kwargs)
4554

@@ -60,7 +69,5 @@ async def get_default(
6069
"""
6170
connections = super().list(connection_type=connection_type, default_connection=True, **kwargs)
6271
async for connection in connections:
63-
if include_credentials:
64-
connection = await super()._get_with_credentials(connection.name, **kwargs)
65-
return connection
72+
return await self.get(connection.name, include_credentials=include_credentials, **kwargs)
6673
raise ValueError(f"No default connection found for type: {connection_type}.")

sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -766,14 +766,10 @@ class CustomCredential(BaseCredentials, discriminator="CustomKeys"):
766766
767767
:ivar type: The credential type. Required. Custom credential
768768
:vartype type: str or ~azure.ai.projects.models.CUSTOM
769-
:ivar credential_keys: The credential type. Required.
770-
:vartype credential_keys: dict[str, str]
771769
"""
772770

773771
type: Literal[CredentialType.CUSTOM] = rest_discriminator(name="type", visibility=["read"]) # type: ignore
774772
"""The credential type. Required. Custom credential"""
775-
credential_keys: Dict[str, str] = rest_field(name="keys", visibility=["read"])
776-
"""The credential type. Required."""
777773

778774
@overload
779775
def __init__(

sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,27 @@
66
77
Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize
88
"""
9-
from typing import List
9+
from typing import List, Dict
1010
from ._patch_evaluations import EvaluatorIds
11+
from ._models import CustomCredential as CustomCredentialGenerated
12+
13+
14+
class CustomCredential(CustomCredentialGenerated):
15+
"""Custom credential definition.
16+
17+
:ivar type: The credential type. Required. Custom credential
18+
:vartype type: str or ~azure.ai.projects.models.CUSTOM
19+
:ivar credential_keys: The credential type. Required.
20+
:vartype credential_keys: dict[str, str]
21+
"""
22+
23+
credential_keys: Dict[str, str] = {}
24+
"""The secret custom credential keys. Required."""
25+
1126

1227
__all__: List[str] = [
1328
"EvaluatorIds",
29+
"CustomCredential",
1430
] # Add all objects you want publicly available to users at this package level
1531

1632

sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch_connections.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,38 @@ def get(self, name: str, *, include_credentials: Optional[bool] = False, **kwarg
3737
:raises ~azure.core.exceptions.HttpResponseError:
3838
"""
3939
if include_credentials:
40-
return super()._get_with_credentials(name, **kwargs)
40+
connection = super()._get_with_credentials(name, **kwargs)
41+
if connection.type == ConnectionType.CUSTOM:
42+
# Fix for GitHub issue https://github.com/Azure/azure-sdk-for-net/issues/52355
43+
# Although the issue was filed on C# Projects SDK, the same problem exists in Python SDK.
44+
# Assume your Foundry project has a connection of type `Custom`, named "test_custom_connection",
45+
# and you defined two public and two secrete (private) keys. When you get the connection, the response
46+
# payload will look something like this:
47+
# {
48+
# "name": "test_custom_connection",
49+
# "id": "/subscriptions/.../connections/test_custom_connection",
50+
# "type": "CustomKeys",
51+
# "target": "_",
52+
# "isDefault": true,
53+
# "credentials": {
54+
# "nameofprivatekey1": "PrivateKey1",
55+
# "nameofprivatekey2": "PrivateKey2",
56+
# "type": "CustomKeys"
57+
# },
58+
# "metadata": {
59+
# "NameOfPublicKey1": "PublicKey1",
60+
# "NameOfPublicKey2": "PublicKey2"
61+
# }
62+
# }
63+
# We would like to add a new Dict property on the Python `credentials` object, named `credential_keys`,
64+
# to hold all the secret keys. This is done by the line below.
65+
setattr(
66+
connection.credentials,
67+
"credential_keys",
68+
{k: v for k, v in connection.credentials.as_dict().items() if k != "type"},
69+
)
70+
71+
return connection
4172

4273
return super()._get(name, **kwargs)
4374

@@ -58,7 +89,5 @@ def get_default(
5889
"""
5990
connections = super().list(connection_type=connection_type, default_connection=True, **kwargs)
6091
for connection in connections:
61-
if include_credentials:
62-
connection = super()._get_with_credentials(connection.name, **kwargs)
63-
return connection
92+
return self.get(connection.name, include_credentials=include_credentials, **kwargs)
6493
raise ValueError(f"No default connection found for type: {connection_type}.")

sdk/ai/azure-ai-projects/tests/test_base.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from azure.ai.projects.models import (
1111
Connection,
1212
ConnectionType,
13+
CustomCredential,
1314
CredentialType,
1415
ApiKeyCredentials,
1516
Deployment,
@@ -41,8 +42,8 @@ class TestBase(AzureRecordedTestCase):
4142
}
4243

4344
test_connections_params = {
44-
"connection_name": "connection1",
45-
"connection_type": ConnectionType.AZURE_OPEN_AI,
45+
"connection_name": "custom_keys_connection",
46+
"connection_type": ConnectionType.CUSTOM,
4647
}
4748

4849
test_deployments_params = {
@@ -117,10 +118,15 @@ def validate_connection(
117118
if expected_is_default is not None:
118119
assert connection.is_default == expected_is_default
119120

120-
if include_credentials:
121-
if type(connection.credentials) == ApiKeyCredentials:
122-
assert connection.credentials.type == CredentialType.API_KEY
121+
if isinstance(connection.credentials, ApiKeyCredentials):
122+
assert connection.credentials.type == CredentialType.API_KEY
123+
if include_credentials:
123124
assert connection.credentials.api_key is not None
125+
elif isinstance(connection.credentials, CustomCredential):
126+
assert connection.credentials.type == CredentialType.CUSTOM
127+
if include_credentials:
128+
assert TestBase.is_valid_dict(connection.credentials.credential_keys)
129+
124130

125131
@classmethod
126132
def validate_red_team_response(cls, response, expected_attack_strategies: int = -1, expected_risk_categories: int = -1):

0 commit comments

Comments
 (0)