Skip to content

Commit 3ef2746

Browse files
author
Abel Milash
committed
Merge remote-tracking branch 'origin/main' into users/abelmilash/module-level-exports
2 parents 753e363 + 91ada39 commit 3ef2746

9 files changed

Lines changed: 1851 additions & 1397 deletions

File tree

examples/basic/functional_testing.py

Lines changed: 164 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import sys
2929
import time
30+
from enum import Enum
3031
from typing import Optional, Dict, Any
3132
from datetime import datetime
3233

@@ -910,7 +911,12 @@ def test_batch_all_operations(client: DataverseClient, table_info: Dict[str, Any
910911
pass
911912

912913

913-
def cleanup_test_data(client: DataverseClient, table_info: Dict[str, Any], record_id: str) -> None:
914+
def cleanup_test_data(
915+
client: DataverseClient,
916+
table_info: Dict[str, Any],
917+
record_id: str,
918+
picklist_table_schema_name: Optional[str] = None,
919+
) -> None:
914920
"""Clean up test data."""
915921
print("\n-> Cleanup")
916922
print("=" * 50)
@@ -980,6 +986,46 @@ def cleanup_test_data(client: DataverseClient, table_info: Dict[str, Any], recor
980986
else:
981987
print("Test table kept for future testing")
982988

989+
# --- Picklist test table cleanup ---
990+
if picklist_table_schema_name:
991+
picklist_cleanup = (
992+
input(f"Do you want to delete the picklist test table '{picklist_table_schema_name}'? (y/N): ")
993+
.strip()
994+
.lower()
995+
)
996+
if picklist_cleanup in ["y", "yes"]:
997+
for attempt in range(1, retries + 1):
998+
try:
999+
client.tables.delete(picklist_table_schema_name)
1000+
print(f"[OK] Picklist test table '{picklist_table_schema_name}' deleted successfully")
1001+
break
1002+
except HttpError as err:
1003+
status = getattr(err, "status_code", None)
1004+
if status == 404:
1005+
if _table_still_exists(client, picklist_table_schema_name):
1006+
if attempt < retries:
1007+
print(
1008+
f" Picklist table delete retry {attempt}/{retries} after metadata 404 ({err}). Waiting {delay_seconds}s..."
1009+
)
1010+
time.sleep(delay_seconds)
1011+
continue
1012+
print(f"[WARN] Failed to delete picklist test table due to metadata delay: {err}")
1013+
break
1014+
print("[OK] Picklist test table deleted successfully (404 reported).")
1015+
break
1016+
if attempt < retries:
1017+
print(
1018+
f" Picklist table delete retry {attempt}/{retries} after error ({err}). Waiting {delay_seconds}s..."
1019+
)
1020+
time.sleep(delay_seconds)
1021+
continue
1022+
print(f"[WARN] Failed to delete picklist test table: {err}")
1023+
except Exception as e:
1024+
print(f"[WARN] Failed to delete picklist test table: {e}")
1025+
break
1026+
else:
1027+
print("Picklist test table kept for future testing")
1028+
9831029

9841030
def backoff(op, *, delays=(0, 2, 5, 10, 20, 20)):
9851031
"""Retry helper with exponential backoff for metadata propagation delays."""
@@ -1261,6 +1307,118 @@ def _get_or_create(schema, columns, label):
12611307
print(f" [WARN] Could not delete {tbl}: {e}")
12621308

12631309

1310+
def test_picklist_table(client: DataverseClient) -> str:
1311+
"""Create a table with a local picklist column and write/read records.
1312+
1313+
Demonstrates:
1314+
- Defining a local OptionSet via an ``Enum`` subclass passed as the column ``dtype``.
1315+
- Optional multi-language labels via the ``__labels__`` class attribute.
1316+
- Writing records using either the enum member's integer value OR its label.
1317+
- Reading the integer value back, and the formatted label via
1318+
``include_annotations="OData.Community.Display.V1.FormattedValue"``.
1319+
1320+
Returns the schema name of the table so the caller can clean it up later.
1321+
"""
1322+
print("\n-> Picklist Column Test")
1323+
print("=" * 50)
1324+
1325+
table_schema_name = "test_PicklistAttribute"
1326+
1327+
# Define a local option set as an Enum. Optional __labels__ provides
1328+
# display labels per language code (1033 = English, 1036 = French).
1329+
class TaskStatus(Enum):
1330+
NotStarted = 1
1331+
InProgress = 2
1332+
Completed = 3
1333+
Cancelled = 4
1334+
1335+
__labels__ = {
1336+
1033: {
1337+
"NotStarted": "Not Started",
1338+
"InProgress": "In Progress",
1339+
"Completed": "Completed",
1340+
"Cancelled": "Cancelled",
1341+
},
1342+
1036: {
1343+
"NotStarted": "Non commencé",
1344+
"InProgress": "En cours",
1345+
"Completed": "Terminé",
1346+
"Cancelled": "Annulé",
1347+
},
1348+
}
1349+
1350+
record_id: Optional[str] = None
1351+
try:
1352+
# Drop any leftover table from a prior failed run so this example is idempotent.
1353+
try:
1354+
existing = client.tables.get(table_schema_name)
1355+
if existing:
1356+
print(f" Removing leftover '{table_schema_name}' from a previous run...")
1357+
client.tables.delete(table_schema_name)
1358+
except Exception:
1359+
pass
1360+
1361+
print(f"Creating table '{table_schema_name}' with a picklist column 'test_status'...")
1362+
1363+
client.tables.create(
1364+
table_schema_name,
1365+
primary_column="test_name",
1366+
columns={
1367+
"test_status": TaskStatus, # Enum subclass => local picklist
1368+
"test_notes": "string",
1369+
},
1370+
)
1371+
1372+
table_info = wait_for_table_metadata(client, table_schema_name)
1373+
print(f"[OK] Picklist table ready: entity_set='{table_info.get('entity_set_name')}'")
1374+
1375+
# --- Insert one record using the enum's integer value ---
1376+
rec_by_int = {
1377+
"test_name": f"Picklist Int {datetime.now().strftime('%H:%M:%S')}",
1378+
"test_status": TaskStatus.InProgress.value, # integer 2
1379+
"test_notes": "Created using TaskStatus.InProgress.value",
1380+
}
1381+
record_id = client.records.create(table_schema_name, rec_by_int)
1382+
print(f"[OK] Created record by int value: {record_id} (status={TaskStatus.InProgress.value})")
1383+
1384+
# --- Insert another record using the picklist label (SDK resolves label -> int) ---
1385+
rec_by_label = {
1386+
"test_name": f"Picklist Label {datetime.now().strftime('%H:%M:%S')}",
1387+
"test_status": "Completed", # resolved via label cache to int 3
1388+
"test_notes": "Created using label string 'Completed'",
1389+
}
1390+
record_id_2 = client.records.create(table_schema_name, rec_by_label)
1391+
print(f"[OK] Created record by label: {record_id_2} (status='Completed' -> 3)")
1392+
1393+
# --- Read back including the FormattedValue annotation ---
1394+
annotation = "OData.Community.Display.V1.FormattedValue"
1395+
retrieved = client.records.retrieve(
1396+
table_schema_name,
1397+
record_id,
1398+
select=["test_name", "test_status"],
1399+
include_annotations=annotation,
1400+
)
1401+
status_int = retrieved.get("test_status")
1402+
status_label = retrieved.get(f"test_status@{annotation}")
1403+
print(f" Retrieved: test_status={status_int}, formatted='{status_label}'")
1404+
assert status_int == TaskStatus.InProgress.value, f"expected {TaskStatus.InProgress.value}, got {status_int}"
1405+
1406+
# --- List records, filtering by the picklist column ---
1407+
completed = client.records.list(
1408+
table_schema_name,
1409+
select=["test_name", "test_status"],
1410+
filter=f"test_status eq {TaskStatus.Completed.value}",
1411+
include_annotations=annotation,
1412+
)
1413+
print(f"[OK] Query by picklist value found {len(completed)} 'Completed' record(s).")
1414+
1415+
except HttpError as e:
1416+
print(f"[ERR] HTTP error during picklist test: {e}")
1417+
raise
1418+
1419+
return table_schema_name
1420+
1421+
12641422
def _table_still_exists(client: DataverseClient, table_schema_name: Optional[str]) -> bool:
12651423
if not table_schema_name:
12661424
return False
@@ -1305,6 +1463,9 @@ def main():
13051463
# Test querying
13061464
test_query_records(client, table_info)
13071465

1466+
# Test picklist (local OptionSet) column creation, write, read
1467+
picklist_table_info = test_picklist_table(client)
1468+
13081469
# Test relationships
13091470
test_relationships(client)
13101471

@@ -1322,13 +1483,14 @@ def main():
13221483
print("[OK] Record Creation: Success")
13231484
print("[OK] Record Reading: Success")
13241485
print("[OK] Record Querying: Success")
1486+
print("[OK] Picklist Column: Success")
13251487
print("[OK] Relationship Operations: Success")
13261488
print("[OK] SQL Encoding: Success")
13271489
print("[OK] Batch Operations: Success")
13281490
print("\nYour PowerPlatform Dataverse Client SDK is fully functional!")
13291491

13301492
# Cleanup
1331-
cleanup_test_data(client, table_info, record_id)
1493+
cleanup_test_data(client, table_info, record_id, picklist_table_info)
13321494

13331495
except KeyboardInterrupt:
13341496
print("\n\n[WARN] Test interrupted by user")

0 commit comments

Comments
 (0)