Skip to content

Commit 5811e0c

Browse files
Merge pull request #2 from microsoft/users/zhaodongwang/smallUpdates
small updates: added azure related library requirements; updated read…
2 parents 6d80dd4 + e5df3e7 commit 5811e0c

4 files changed

Lines changed: 46 additions & 45 deletions

File tree

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,6 @@ Notes:
139139
- For CRUD methods that take a record id, pass the GUID string (36-char hyphenated). Parentheses around the GUID are accepted but not required.
140140
- SQL is routed through the Custom API named in `DataverseConfig.sql_api_name` (default: `McpExecuteSqlQuery`).
141141

142-
143-
144142
### Pandas helpers
145143

146144
See `examples/quickstart_pandas.py` for a DataFrame workflow via `PandasODataClient`.
@@ -152,6 +150,7 @@ VS Code Tasks
152150
## Limitations / Future Work
153151
- No batching, upsert, or association operations yet.
154152
- Minimal retry policy in library (network-error only); examples include additional backoff for transient Dataverse consistency.
153+
- Entity naming conventions in Dataverse (schema/logical/entity set plural & publisher prefix) using the SDK is currently not well-defined
155154

156155
## Contributing
157156

examples/quickstart.py

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sys
22
from pathlib import Path
3+
import os
34

45
# Add src to PYTHONPATH for local runs
56
sys.path.append(str(Path(__file__).resolve().parents[1] / "src"))
@@ -9,8 +10,15 @@
910
import traceback
1011
import requests
1112
import time
12-
13-
base_url = 'https://aurorabapenv0f528.crm10.dynamics.com'
13+
14+
if not sys.stdin.isatty():
15+
print("Interactive input required for org URL. Run this script in a TTY.")
16+
sys.exit(1)
17+
entered = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip()
18+
if not entered:
19+
print("No URL entered; exiting.")
20+
sys.exit(1)
21+
base_url = entered.rstrip('/')
1422
client = DataverseClient(base_url=base_url, credential=InteractiveBrowserCredential())
1523

1624
# Small helpers: call logging and step pauses
@@ -259,50 +267,34 @@ def print_line_summaries(label: str, summaries: list[dict]) -> None:
259267
# 4) Query records via SQL Custom API
260268
print("Query (SQL via Custom API):")
261269
try:
262-
# Try singular logical name first, then plural entity set, with short backoff
263270
import time
271+
plan_call(f"client.query_sql(\"SELECT TOP 2 * FROM {logical} ORDER BY {attr_prefix}_amount DESC\")")
272+
pause("Execute SQL Query")
264273

265-
candidates = [logical]
266-
if entity_set and entity_set != logical:
267-
candidates.append(entity_set)
274+
def _run_query():
275+
log_call(f"client.query_sql(\"SELECT TOP 2 * FROM {logical} ORDER BY {attr_prefix}_amount DESC\")")
276+
return client.query_sql(f"SELECT TOP 2 * FROM {logical} ORDER BY {attr_prefix}_amount DESC")
268277

269-
# Show planned SQL queries before executing
270-
for name in candidates:
271-
plan_call(f"client.query_sql(\"SELECT TOP 2 * FROM {name} ORDER BY {attr_prefix}_amount DESC\")")
272-
pause("Execute SQL Query")
278+
def _retry_if(ex: Exception) -> bool:
279+
msg = str(ex) if ex else ""
280+
return ("Invalid table name" in msg) or ("Invalid object name" in msg)
273281

274-
rows = []
275-
for name in candidates:
276-
def _run_query():
277-
log_call(f"client.query_sql(\"SELECT TOP 2 * FROM {name} ORDER BY {attr_prefix}_amount DESC\")")
278-
return client.query_sql(f"SELECT TOP 2 * FROM {name} ORDER BY {attr_prefix}_amount DESC")
279-
def _retry_if(ex: Exception) -> bool:
280-
msg = str(ex) if ex else ""
281-
return ("Invalid table name" in msg) or ("Invalid object name" in msg)
282-
try:
283-
rows = backoff_retry(_run_query, delays=(0, 2, 5), retry_http_statuses=(), retry_if=_retry_if)
284-
logical_for_ids = logical
285-
id_key = f"{logical_for_ids}id"
286-
ids = [r.get(id_key) for r in rows if isinstance(r, dict) and r.get(id_key)]
287-
print({"entity": name, "rows": len(rows) if isinstance(rows, list) else 0, "ids": ids})
288-
# Print TDS summaries for clarity
289-
tds_summaries = []
290-
for row in rows if isinstance(rows, list) else []:
291-
tds_summaries.append(
292-
{
293-
"id": row.get(id_key),
294-
"code": row.get(code_key),
295-
"count": row.get(count_key),
296-
"amount": row.get(amount_key),
297-
"when": row.get(when_key),
298-
}
299-
)
300-
print_line_summaries("TDS record summaries (top 2 by amount):", tds_summaries)
301-
raise SystemExit
302-
except Exception:
303-
continue
304-
except SystemExit:
305-
pass
282+
rows = backoff_retry(_run_query, delays=(0, 2, 5), retry_http_statuses=(), retry_if=_retry_if)
283+
id_key = f"{logical}id"
284+
ids = [r.get(id_key) for r in rows if isinstance(r, dict) and r.get(id_key)]
285+
print({"entity": logical, "rows": len(rows) if isinstance(rows, list) else 0, "ids": ids})
286+
tds_summaries = []
287+
for row in rows if isinstance(rows, list) else []:
288+
tds_summaries.append(
289+
{
290+
"id": row.get(id_key),
291+
"code": row.get(code_key),
292+
"count": row.get(count_key),
293+
"amount": row.get(amount_key),
294+
"when": row.get(when_key),
295+
}
296+
)
297+
print_line_summaries("TDS record summaries (top 2 by amount):", tds_summaries)
306298
except Exception as e:
307299
print(f"SQL via Custom API failed: {e}")
308300
# 5) Delete record

examples/quickstart_pandas.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sys
22
from pathlib import Path
3+
import os
34

45
# Add src to PYTHONPATH for local runs
56
sys.path.append(str(Path(__file__).resolve().parents[1] / "src"))
@@ -12,7 +13,14 @@
1213
import time
1314
import pandas as pd
1415

15-
base_url = 'https://aurorabapenv0f528.crm10.dynamics.com'
16+
if not sys.stdin.isatty():
17+
print("Interactive input required for org URL. Run this script in a TTY.")
18+
sys.exit(1)
19+
entered = input("Enter Dataverse org URL (e.g. https://yourorg.crm.dynamics.com): ").strip()
20+
if not entered:
21+
print("No URL entered; exiting.")
22+
sys.exit(1)
23+
base_url = entered.rstrip('/')
1624
client = DataverseClient(base_url=base_url, credential=InteractiveBrowserCredential())
1725
# Use the internal OData client for pandas helpers
1826
PANDAS = PandasODataClient(client._get_odata())

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
azure-identity>=1.17.0
2+
azure-core>=1.30.2
13
msal>=1.28.0
24
requests>=2.32.0
35
pyodbc>=5.1.0

0 commit comments

Comments
 (0)