Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions src/PowerPlatform/Dataverse/data/_odata.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,25 @@ def _normalize_cache_key(table_schema_name: str) -> str:

@staticmethod
def _lowercase_keys(record: Dict[str, Any]) -> Dict[str, Any]:
"""Convert all dictionary keys to lowercase for case-insensitive column names.
"""Normalize dictionary keys to lowercase for case-insensitive column names.

Dataverse LogicalNames for attributes are stored lowercase, but users may
provide PascalCase names (matching SchemaName). This normalizes the input.
This function lowercases all string keys except OData annotation keys,
which must remain case-sensitive.
"""
if not isinstance(record, dict):
return record
return {k.lower() if isinstance(k, str) else k: v for k, v in record.items()}

new_record = {}

# Preserve OData annotation keys as they are case sensitive
for k, v in record.items():
if isinstance(k, str) and "@odata" in k.lower():
new_record[k] = v
else:
new_record[k.lower() if isinstance(k, str) else k] = v
return new_record

@staticmethod
def _lowercase_list(items: Optional[List[str]]) -> Optional[List[str]]:
Expand Down Expand Up @@ -269,7 +280,7 @@ def _create(self, entity_set: str, table_schema_name: str, record: Dict[str, Any
.. note::
Relies on ``OData-EntityId`` (canonical) or ``Location`` response header. No response body parsing is performed. Raises ``RuntimeError`` if neither header contains a GUID.
"""
# Lowercase all keys to match Dataverse LogicalName expectations
# Lowercase Dataverse attribute keys; keep OData annotation keys unchanged
record = self._lowercase_keys(record)
record = self._convert_labels_to_ints(table_schema_name, record)
url = f"{self.api}/{entity_set}"
Expand Down Expand Up @@ -313,7 +324,7 @@ def _create_multiple(self, entity_set: str, table_schema_name: str, records: Lis
logical_name = table_schema_name.lower()
enriched: List[Dict[str, Any]] = []
for r in records:
# Lowercase all keys to match Dataverse LogicalName expectations
# Lowercase Dataverse attribute keys; keep OData annotation keys unchanged
r = self._lowercase_keys(r)
r = self._convert_labels_to_ints(table_schema_name, r)
if "@odata.type" in r or not need_logical:
Expand Down Expand Up @@ -506,7 +517,7 @@ def _update(self, table_schema_name: str, key: str, data: Dict[str, Any]) -> Non
:return: ``None``
:rtype: ``None``
"""
# Lowercase all keys to match Dataverse LogicalName expectations
# Lowercase Dataverse attribute keys; keep OData annotation keys unchanged
data = self._lowercase_keys(data)
data = self._convert_labels_to_ints(table_schema_name, data)
entity_set = self._entity_set_from_schema_name(table_schema_name)
Expand Down Expand Up @@ -540,7 +551,7 @@ def _update_multiple(self, entity_set: str, table_schema_name: str, records: Lis
logical_name = table_schema_name.lower()
enriched: List[Dict[str, Any]] = []
for r in records:
# Lowercase all keys to match Dataverse LogicalName expectations
# Lowercase Dataverse attribute keys; keep OData annotation keys unchanged
r = self._lowercase_keys(r)
r = self._convert_labels_to_ints(table_schema_name, r)
if "@odata.type" in r or not need_logical:
Expand Down