Skip to content

Commit 59ef366

Browse files
author
Max Wang
committed
clean up changes
1 parent b07e5ae commit 59ef366

4 files changed

Lines changed: 17 additions & 136 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ A Python package allowing developers to connect to Dataverse environments for DD
66
- OData CRUD — Unified methods `create(logical_name, record|records)`, `update(logical_name, id|ids, patch|patches)`, `delete(logical_name, id|ids)` plus `get` with record id or filters and `delete_async` for better multi-record delete performance.
77
- Bulk create — Pass a list of records to `create(...)` to invoke the bound `CreateMultiple` action; returns `list[str]` of GUIDs. If any payload omits `@odata.type` the SDK resolves and stamps it (cached).
88
- Bulk update — Provide a list of IDs with a single patch (broadcast) or a list of per‑record patches to `update(...)`; internally uses the bound `UpdateMultiple` action; returns nothing. Each record must include the primary key attribute when sent to UpdateMultiple.
9+
- Bulk delete - Provide a list of IDs to `delete(...)` or `delete_async(...)`. `delete` internally uses `DeleteMultiple` for elastic tables, for standard tables it is a loop over single-record delete. `delete_async` interally uses async BulkDelete.
910
- Retrieve multiple (paging) — Generator-based `get(...)` that yields pages, supports `$top` and Prefer: `odata.maxpagesize` (`page_size`).
1011
- Upload files — Call `upload_file(logical_name, ...)` and an upload method will be auto picked (you can override the mode). See https://learn.microsoft.com/en-us/power-apps/developer/data-platform/file-column-data?tabs=sdk#upload-files
1112
- Metadata helpers — Create/inspect/delete tables and create/delete columns (EntityDefinitions + Attributes).
@@ -145,7 +146,7 @@ print({"multi_update": "ok"})
145146
# Delete (single)
146147
client.delete("account", account_id)
147148

148-
# Delete multiple sequentially
149+
# Delete (multiple)
149150
client.delete("account", ids)
150151

151152
# Or queue a async bulk delete job

examples/quickstart.py

Lines changed: 0 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@
3333
# Create a credential we can reuse (for DataverseClient)
3434
credential = InteractiveBrowserCredential()
3535
client = DataverseClient(base_url=base_url, credential=credential)
36-
elastic_table_schema = "new_ElasticDeleteDemo"
37-
elastic_table_created_this_run = False
38-
elastic_table_logical_name: Optional[str] = None
39-
elastic_table_metadata_id: Optional[str] = None
4036

4137
# Small helpers: call logging and step pauses
4238
def log_call(call: str) -> None:
@@ -73,129 +69,6 @@ def backoff_retry(op, *, delays=(0, 2, 5, 10, 20), retry_http_statuses=(400, 403
7369
break
7470
if last_exc:
7571
raise last_exc
76-
77-
def run_elastic_delete_demo() -> None:
78-
global elastic_table_created_this_run, elastic_table_logical_name, elastic_table_metadata_id
79-
print("Elastic DeleteMultiple demo (elastic table setup):")
80-
odata_client = client._get_odata()
81-
schema_name = elastic_table_schema
82-
publisher_prefix = schema_name.split("_", 1)[0] if "_" in schema_name else schema_name
83-
primary_attr = f"{publisher_prefix}_Name"
84-
count_attr = f"{publisher_prefix}_Count"
85-
flag_attr = f"{publisher_prefix}_Flag"
86-
87-
def _fetch_metadata() -> Optional[dict]:
88-
url = f"{odata_client.api}/EntityDefinitions"
89-
params = {
90-
"$select": "MetadataId,LogicalName,EntitySetName,SchemaName,TableType",
91-
"$filter": f"SchemaName eq '{schema_name}'",
92-
}
93-
r = odata_client._request("get", url, params=params)
94-
try:
95-
body = r.json() if r.text else {}
96-
except ValueError:
97-
return None
98-
items = body.get("value") if isinstance(body, dict) else None
99-
if isinstance(items, list) and items:
100-
md = items[0]
101-
return md if isinstance(md, dict) else None
102-
return None
103-
104-
try:
105-
metadata = _fetch_metadata()
106-
if metadata and str(metadata.get("TableType", "")).lower() != "elastic":
107-
print({
108-
"elastic_table": schema_name,
109-
"skipped": True,
110-
"reason": "Existing table is not elastic; DeleteMultiple demo not run",
111-
"table_type": metadata.get("TableType"),
112-
})
113-
return
114-
if not metadata:
115-
log_call(f"POST EntityDefinitions (TableType=Elastic) for {schema_name}")
116-
attributes = [
117-
odata_client._attribute_payload(primary_attr, "string", is_primary_name=True),
118-
odata_client._attribute_payload(count_attr, "int"),
119-
odata_client._attribute_payload(flag_attr, "bool"),
120-
]
121-
attrs = [a for a in attributes if a]
122-
payload = {
123-
"@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata",
124-
"SchemaName": schema_name,
125-
"DisplayName": odata_client._label("Elastic Delete Demo"),
126-
"DisplayCollectionName": odata_client._label("Elastic Delete Demos"),
127-
"Description": odata_client._label("Elastic table for DeleteMultiple quickstart validation"),
128-
"OwnershipType": "UserOwned",
129-
"HasActivities": False,
130-
"HasNotes": True,
131-
"IsEnabledForCharts": False,
132-
"IsVirtualEntityReportingEnabled": False,
133-
"IsActivity": False,
134-
"TableType": "Elastic",
135-
"Attributes": attrs,
136-
}
137-
def _create_elastic():
138-
odata_client._request("post", f"{odata_client.api}/EntityDefinitions", json=payload)
139-
return True
140-
try:
141-
backoff_retry(_create_elastic)
142-
except Exception as create_exc:
143-
print({
144-
"elastic_table": schema_name,
145-
"skipped": True,
146-
"reason": "Elastic table creation failed",
147-
"error": str(create_exc),
148-
})
149-
return
150-
ready = backoff_retry(lambda: odata_client._wait_for_entity_ready(schema_name), retry_http_statuses=())
151-
if not ready or not ready.get("MetadataId"):
152-
raise RuntimeError("Elastic demo table metadata not ready")
153-
metadata = _fetch_metadata()
154-
elastic_table_created_this_run = True
155-
if not metadata:
156-
print({"elastic_table": schema_name, "skipped": True, "reason": "Metadata unavailable"})
157-
return
158-
elastic_table_logical_name = metadata.get("LogicalName")
159-
elastic_table_metadata_id = metadata.get("MetadataId")
160-
odata_client._elastic_table_cache.pop(elastic_table_logical_name, None)
161-
odata_client._logical_to_entityset_cache.pop(elastic_table_logical_name, None)
162-
odata_client._logical_primaryid_cache.pop(elastic_table_logical_name, None)
163-
is_elastic = odata_client._is_elastic_table(elastic_table_logical_name) if elastic_table_logical_name else False
164-
print({
165-
"elastic_table": schema_name,
166-
"logical_name": elastic_table_logical_name,
167-
"table_type_reported": metadata.get("TableType"),
168-
"is_elastic": bool(is_elastic),
169-
})
170-
logical = elastic_table_logical_name
171-
if not logical:
172-
print({"elastic_table": schema_name, "skipped": True, "reason": "Logical name missing"})
173-
return
174-
prefix = logical.split("_", 1)[0] if "_" in logical else logical
175-
name_key = f"{prefix}_name"
176-
count_key = f"{prefix}_count"
177-
flag_key = f"{prefix}_flag"
178-
records = [
179-
{name_key: "Elastic Demo A", count_key: 10, flag_key: True},
180-
{name_key: "Elastic Demo B", count_key: 11, flag_key: False},
181-
{name_key: "Elastic Demo C", count_key: 12, flag_key: True},
182-
]
183-
log_call(f"client.create('{logical}', <{len(records)} elastic records>)")
184-
created_ids = backoff_retry(lambda: client.create(logical, records))
185-
valid_ids = [rid for rid in created_ids if isinstance(rid, str)] if isinstance(created_ids, list) else []
186-
if not valid_ids:
187-
raise RuntimeError("Elastic demo record creation returned no GUIDs")
188-
print({"elastic_create_ids": valid_ids})
189-
log_call(f"client.delete('{logical}', <{len(valid_ids)} elastic ids>)")
190-
backoff_retry(lambda: client.delete(logical, valid_ids))
191-
print({
192-
"elastic_delete_multiple": {
193-
"requested": len(valid_ids),
194-
"succeeded": True,
195-
}
196-
})
197-
except Exception as exc:
198-
print({"elastic_demo_error": str(exc)})
19972

20073
# Enum demonstrating local option set creation with multilingual labels (for French labels to work, enable French language in the environment first)
20174
class Status(IntEnum):
@@ -215,10 +88,6 @@ class Status(IntEnum):
21588
}
21689
}
21790

218-
pause("Run elastic DeleteMultiple demo")
219-
run_elastic_delete_demo()
220-
sys.exit(0)
221-
22291
print("Ensure custom table exists (Metadata):")
22392
table_info = None
22493
created_this_run = False

src/dataverse_sdk/client.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .config import DataverseConfig
1212
from .odata import ODataClient
1313

14+
1415
class DataverseClient:
1516
"""
1617
High-level client for Microsoft Dataverse operations.
@@ -219,9 +220,6 @@ def delete(
219220
:raises TypeError: If ``ids`` is not str or list[str].
220221
:raises HttpError: If the underlying Web API delete request fails.
221222
222-
:return: ``None`` once the requested records have been deleted sequentially.
223-
:rtype: None
224-
225223
Example:
226224
Delete a single record::
227225

src/dataverse_sdk/odata.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,20 @@ def _update_by_ids(self, logical_name: str, ids: List[str], changes: Union[Dict[
288288
return None
289289

290290
def _delete_multiple(self, logical_name: str, ids: List[str]) -> None:
291-
"""Delete many records synchronously via the DeleteMultiple collection action."""
291+
"""Delete records using the collection-bound DeleteMultiple action.
292+
293+
Parameters
294+
----------
295+
logical_name : str
296+
Singular logical entity name.
297+
ids : list[str]
298+
GUIDs for the records to remove.
299+
300+
Returns
301+
-------
302+
None
303+
No representation is returned.
304+
"""
292305
entity_set = self._entity_set_from_logical(logical_name)
293306
pk_attr = self._primary_id_attr(logical_name)
294307
targets: List[Dict[str, Any]] = []

0 commit comments

Comments
 (0)