Skip to content

Commit 8a26fd7

Browse files
author
Saurabh Badenkal
committed
Expose primary_name_attribute in TableInfo metadata (fixes #148)
SDK changes: - _get_entity_by_table_schema_name selects PrimaryNameAttribute and PrimaryIdAttribute from EntityDefinitions - _create_table and _get_table_info return primary_name_attribute - TableInfo model includes primary_name_attribute and primary_id_attribute fields with legacy dict-key access - Tests for from_dict, from_api_response, and legacy key access This allows tables.create() and tables.get() callers to discover the actual primary column logical name instead of guessing from the prefix.
1 parent 5523b9d commit 8a26fd7

4 files changed

Lines changed: 57 additions & 37 deletions

File tree

examples/advanced/datascience_risk_assessment.py

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -215,41 +215,16 @@ def complete(system_prompt, user_prompt):
215215

216216

217217
def _init_copilot_sdk():
218-
"""Initialize GitHub Copilot SDK (uses existing Copilot subscription)."""
219-
try:
220-
from copilot import CopilotClient
221-
except ImportError:
222-
return None
223-
224-
import asyncio
225-
import shutil
226-
227-
# Find the Copilot CLI binary (may not be bundled in source installs)
228-
cli_path = shutil.which("copilot")
229-
if cli_path and not cli_path.endswith((".ps1", ".bat", ".cmd")):
230-
copilot_kwargs = {"cli_path": cli_path}
231-
else:
232-
copilot_kwargs = {} # let the SDK try its default lookup
233-
234-
async def _copilot_complete(prompt_text):
235-
async with CopilotClient(**copilot_kwargs) as client:
236-
session = await client.create_session()
237-
events = []
238-
session.on(lambda e: events.append(e))
239-
await session.send({"prompt": prompt_text})
240-
# Collect text from response events
241-
text_parts = []
242-
for e in events:
243-
if hasattr(e, "type") and e.type == "content" and hasattr(e, "body"):
244-
text_parts.append(e.body)
245-
return "".join(text_parts).strip()
246-
247-
def complete(system_prompt, user_prompt):
248-
combined = f"{system_prompt}\n\n{user_prompt}"
249-
return asyncio.run(_copilot_complete(combined))
218+
"""Initialize GitHub Copilot SDK.
250219
251-
print("[INFO] LLM provider: GitHub Copilot SDK")
252-
return _wrap_with_logging(complete, "GitHub Copilot SDK", "copilot")
220+
# Uncomment and configure to use your Copilot subscription as the LLM provider.
221+
# Requires: pip install github-copilot-sdk
222+
# Copilot CLI must be installed. See: https://github.com/github/copilot-sdk
223+
"""
224+
# To enable, install the SDK and uncomment the implementation below.
225+
# from copilot import CopilotClient
226+
# ... (see Copilot SDK docs for session/send_and_wait usage)
227+
return None
253228

254229

255230
# ================================================================

src/PowerPlatform/Dataverse/data/_odata.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -960,7 +960,7 @@ def _get_entity_by_table_schema_name(
960960
logical_lower = table_schema_name.lower()
961961
logical_escaped = self._escape_odata_quotes(logical_lower)
962962
params = {
963-
"$select": "MetadataId,LogicalName,SchemaName,EntitySetName",
963+
"$select": "MetadataId,LogicalName,SchemaName,EntitySetName,PrimaryNameAttribute,PrimaryIdAttribute",
964964
"$filter": f"LogicalName eq '{logical_escaped}'",
965965
}
966966
r = self._request("get", url, params=params, headers=headers)
@@ -1445,6 +1445,8 @@ def _get_table_info(self, table_schema_name: str) -> Optional[Dict[str, Any]]:
14451445
"table_logical_name": ent.get("LogicalName"),
14461446
"entity_set_name": ent.get("EntitySetName"),
14471447
"metadata_id": ent.get("MetadataId"),
1448+
"primary_name_attribute": ent.get("PrimaryNameAttribute"),
1449+
"primary_id_attribute": ent.get("PrimaryIdAttribute"),
14481450
"columns_created": [],
14491451
}
14501452

@@ -1689,6 +1691,8 @@ def _create_table(
16891691
"table_logical_name": metadata.get("LogicalName"),
16901692
"entity_set_name": metadata.get("EntitySetName"),
16911693
"metadata_id": metadata.get("MetadataId"),
1694+
"primary_name_attribute": metadata.get("PrimaryNameAttribute"),
1695+
"primary_id_attribute": metadata.get("PrimaryIdAttribute"),
16921696
"columns_created": created_cols,
16931697
}
16941698

src/PowerPlatform/Dataverse/models/table_info.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ class TableInfo:
112112
logical_name: str = ""
113113
entity_set_name: str = ""
114114
metadata_id: str = ""
115+
primary_name_attribute: Optional[str] = None
116+
primary_id_attribute: Optional[str] = None
115117
display_name: Optional[str] = None
116118
description: Optional[str] = None
117119
columns: Optional[List[ColumnInfo]] = field(default=None, repr=False)
@@ -123,6 +125,8 @@ class TableInfo:
123125
"table_logical_name": "logical_name",
124126
"entity_set_name": "entity_set_name",
125127
"metadata_id": "metadata_id",
128+
"primary_name_attribute": "primary_name_attribute",
129+
"primary_id_attribute": "primary_id_attribute",
126130
"columns_created": "columns_created",
127131
}
128132

@@ -187,6 +191,8 @@ def from_dict(cls, data: Dict[str, Any]) -> TableInfo:
187191
logical_name=data.get("table_logical_name", ""),
188192
entity_set_name=data.get("entity_set_name", ""),
189193
metadata_id=data.get("metadata_id", ""),
194+
primary_name_attribute=data.get("primary_name_attribute"),
195+
primary_id_attribute=data.get("primary_id_attribute"),
190196
columns_created=data.get("columns_created"),
191197
)
192198

@@ -213,6 +219,8 @@ def from_api_response(cls, response_data: Dict[str, Any]) -> TableInfo:
213219
logical_name=response_data.get("LogicalName", ""),
214220
entity_set_name=response_data.get("EntitySetName", ""),
215221
metadata_id=response_data.get("MetadataId", ""),
222+
primary_name_attribute=response_data.get("PrimaryNameAttribute"),
223+
primary_id_attribute=response_data.get("PrimaryIdAttribute"),
216224
display_name=display_name,
217225
description=description,
218226
)

tests/unit/models/test_table_info.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,19 @@ def test_legacy_key_iteration(self):
4949
keys = list(self.info)
5050
self.assertEqual(
5151
keys,
52-
["table_schema_name", "table_logical_name", "entity_set_name", "metadata_id", "columns_created"],
52+
[
53+
"table_schema_name",
54+
"table_logical_name",
55+
"entity_set_name",
56+
"metadata_id",
57+
"primary_name_attribute",
58+
"primary_id_attribute",
59+
"columns_created",
60+
],
5361
)
5462

5563
def test_len(self):
56-
self.assertEqual(len(self.info), 5)
64+
self.assertEqual(len(self.info), 7)
5765

5866
def test_keys_values_items(self):
5967
self.assertEqual(list(self.info.keys()), list(self.info._LEGACY_KEY_MAP.keys()))
@@ -76,20 +84,41 @@ def test_from_dict(self):
7684
"table_logical_name": "new_product",
7785
"entity_set_name": "new_products",
7886
"metadata_id": "meta-guid-1",
87+
"primary_name_attribute": "new_name",
88+
"primary_id_attribute": "new_productid",
7989
"columns_created": ["new_Price"],
8090
}
8191
info = TableInfo.from_dict(data)
8292
self.assertEqual(info.schema_name, "new_Product")
8393
self.assertEqual(info.logical_name, "new_product")
8494
self.assertEqual(info.entity_set_name, "new_products")
8595
self.assertEqual(info.metadata_id, "meta-guid-1")
96+
self.assertEqual(info.primary_name_attribute, "new_name")
97+
self.assertEqual(info.primary_id_attribute, "new_productid")
8698
self.assertEqual(info.columns_created, ["new_Price"])
8799

88100
def test_from_dict_missing_keys(self):
89101
info = TableInfo.from_dict({})
90102
self.assertEqual(info.schema_name, "")
103+
self.assertIsNone(info.primary_name_attribute)
104+
self.assertIsNone(info.primary_id_attribute)
91105
self.assertIsNone(info.columns_created)
92106

107+
def test_from_dict_legacy_access_primary_fields(self):
108+
"""Primary fields are accessible via legacy dict-key access."""
109+
data = {
110+
"table_schema_name": "new_Product",
111+
"table_logical_name": "new_product",
112+
"entity_set_name": "new_products",
113+
"metadata_id": "meta-guid-1",
114+
"primary_name_attribute": "new_name",
115+
"primary_id_attribute": "new_productid",
116+
"columns_created": [],
117+
}
118+
info = TableInfo.from_dict(data)
119+
self.assertEqual(info["primary_name_attribute"], "new_name")
120+
self.assertEqual(info["primary_id_attribute"], "new_productid")
121+
93122

94123
class TestTableInfoFromApiResponse(unittest.TestCase):
95124
"""Tests for TableInfo.from_api_response factory (PascalCase keys)."""
@@ -100,6 +129,8 @@ def test_from_api_response(self):
100129
"LogicalName": "account",
101130
"EntitySetName": "accounts",
102131
"MetadataId": "meta-guid-2",
132+
"PrimaryNameAttribute": "name",
133+
"PrimaryIdAttribute": "accountid",
103134
"DisplayName": {"UserLocalizedLabel": {"Label": "Account", "LanguageCode": 1033}},
104135
"Description": {"UserLocalizedLabel": {"Label": "Business account", "LanguageCode": 1033}},
105136
}
@@ -108,6 +139,8 @@ def test_from_api_response(self):
108139
self.assertEqual(info.logical_name, "account")
109140
self.assertEqual(info.entity_set_name, "accounts")
110141
self.assertEqual(info.metadata_id, "meta-guid-2")
142+
self.assertEqual(info.primary_name_attribute, "name")
143+
self.assertEqual(info.primary_id_attribute, "accountid")
111144
self.assertEqual(info.display_name, "Account")
112145
self.assertEqual(info.description, "Business account")
113146

0 commit comments

Comments
 (0)